@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,6 @@
1
+ import { Properties } from "@firecms/core";
2
+ export interface EnumConditionsEditorProps {
3
+ disabled: boolean;
4
+ collectionProperties?: Properties;
5
+ }
6
+ export declare function EnumConditionsEditor({ disabled, collectionProperties }: EnumConditionsEditorProps): import("react/jsx-runtime").JSX.Element | null;
@@ -0,0 +1,6 @@
1
+ export { ConditionsPanel } from "./ConditionsPanel";
2
+ export { ConditionsEditor } from "./ConditionsEditor";
3
+ export type { ConditionsEditorProps } from "./ConditionsEditor";
4
+ export { EnumConditionsEditor } from "./EnumConditionsEditor";
5
+ export type { EnumConditionsEditorProps } from "./EnumConditionsEditor";
6
+ export { getPropertyPaths, getGroupedPropertyPaths } from "./property_paths";
@@ -0,0 +1,19 @@
1
+ import { Properties } from "@firecms/core";
2
+ /**
3
+ * Recursively extract all property paths from a Properties object.
4
+ * For nested map properties, creates dot-notation paths like "address.city".
5
+ * Skips PropertyBuilder functions (callbacks) as they cannot be statically analyzed.
6
+ *
7
+ * @param properties - The properties object to extract paths from
8
+ * @param prefix - Optional prefix for nested paths (used in recursion)
9
+ * @returns Array of property path strings
10
+ */
11
+ export declare function getPropertyPaths(properties: Properties | undefined, prefix?: string): string[];
12
+ /**
13
+ * Get property paths grouped by top-level property for UI display.
14
+ * Skips PropertyBuilder functions.
15
+ *
16
+ * @param properties - The properties object
17
+ * @returns Object with top-level keys mapping to their nested paths
18
+ */
19
+ export declare function getGroupedPropertyPaths(properties: Properties | undefined): Record<string, string[]>;
@@ -4,6 +4,7 @@ import { CollectionEditorPermissionsBuilder } from "./types/config_permissions";
4
4
  import { PersistedCollection } from "./types/persisted_collection";
5
5
  import { CollectionInference } from "./types/collection_inference";
6
6
  import { CollectionsConfigController } from "./types/config_controller";
7
+ import { CollectionGenerationCallback } from "./api/generateCollectionApi";
7
8
  export interface CollectionConfigControllerProps<EC extends PersistedCollection = PersistedCollection, USER extends User = User> {
8
9
  /**
9
10
  * Firebase app where the configuration is saved.
@@ -31,6 +32,11 @@ export interface CollectionConfigControllerProps<EC extends PersistedCollection
31
32
  getUser?: (uid: string) => USER | null;
32
33
  onAnalyticsEvent?: (event: string, params?: object) => void;
33
34
  includeIntroView?: boolean;
35
+ /**
36
+ * Callback function for generating/modifying collections.
37
+ * The plugin is API-agnostic - the consumer provides the implementation.
38
+ */
39
+ generateCollection?: CollectionGenerationCallback;
34
40
  }
35
41
  /**
36
42
  * Use this hook to initialise the Collection Editor plugin.
@@ -43,5 +49,5 @@ export interface CollectionConfigControllerProps<EC extends PersistedCollection
43
49
  * @param getUser
44
50
  * @param collectionInference
45
51
  */
46
- export declare function useCollectionEditorPlugin<EC extends PersistedCollection = PersistedCollection, USER extends User = User>({ collectionConfigController, configPermissions, reservedGroups, extraView, getUser, collectionInference, getData, onAnalyticsEvent, includeIntroView, pathSuggestions }: CollectionConfigControllerProps<EC, USER>): FireCMSPlugin<any, any, PersistedCollection>;
52
+ export declare function useCollectionEditorPlugin<EC extends PersistedCollection = PersistedCollection, USER extends User = User>({ collectionConfigController, configPermissions, reservedGroups, extraView, getUser, collectionInference, getData, onAnalyticsEvent, includeIntroView, pathSuggestions, generateCollection }: CollectionConfigControllerProps<EC, USER>): FireCMSPlugin<any, any, PersistedCollection>;
47
53
  export declare function IntroWidget(): import("react/jsx-runtime").JSX.Element | null;
@@ -0,0 +1,22 @@
1
+ import { EntityCollection } from "@firecms/core";
2
+ /**
3
+ * Validation error with path and message
4
+ */
5
+ export interface CollectionValidationError {
6
+ path: string;
7
+ message: string;
8
+ }
9
+ /**
10
+ * Result of collection JSON validation
11
+ */
12
+ export interface CollectionValidationResult {
13
+ valid: boolean;
14
+ errors: CollectionValidationError[];
15
+ collection?: EntityCollection;
16
+ }
17
+ /**
18
+ * Validates a JSON string representing a collection configuration.
19
+ * Returns detailed validation errors if the JSON is invalid or doesn't match
20
+ * the expected collection schema.
21
+ */
22
+ export declare function validateCollectionJson(jsonString: string): CollectionValidationResult;
package/package.json CHANGED
@@ -1,38 +1,38 @@
1
1
  {
2
2
  "name": "@firecms/collection_editor",
3
3
  "type": "module",
4
- "version": "3.0.1",
4
+ "version": "3.1.0-canary.02232f4",
5
5
  "main": "./dist/index.umd.js",
6
6
  "module": "./dist/index.es.js",
7
7
  "types": "./dist/index.d.ts",
8
8
  "source": "src/index.ts",
9
9
  "dependencies": {
10
- "@firecms/data_export": "^3.0.1",
11
- "@firecms/data_import": "^3.0.1",
12
- "@firecms/data_import_export": "^3.0.1",
13
- "@firecms/formex": "^3.0.1",
14
- "@firecms/schema_inference": "^3.0.1",
15
- "@firecms/ui": "^3.0.1",
10
+ "@firecms/data_export": "^3.1.0-canary.02232f4",
11
+ "@firecms/data_import": "^3.1.0-canary.02232f4",
12
+ "@firecms/data_import_export": "^3.1.0-canary.02232f4",
13
+ "@firecms/formex": "^3.1.0-canary.02232f4",
14
+ "@firecms/schema_inference": "^3.1.0-canary.02232f4",
15
+ "@firecms/ui": "^3.1.0-canary.02232f4",
16
16
  "json5": "^2.2.3",
17
17
  "prism-react-renderer": "^2.4.1"
18
18
  },
19
19
  "peerDependencies": {
20
- "react": ">=18.0.0",
21
- "react-dom": ">=18.0.0",
20
+ "react": ">=18.3.1 || >=19.0.0",
21
+ "react-dom": ">=18.3.1 || >=19.0.0",
22
22
  "react-router": "^6.28.0",
23
23
  "react-router-dom": "^6.28.0"
24
24
  },
25
25
  "exports": {
26
26
  ".": {
27
+ "types": "./dist/index.d.ts",
27
28
  "import": "./dist/index.es.js",
28
- "require": "./dist/index.umd.js",
29
- "types": "./dist/index.d.ts"
29
+ "require": "./dist/index.umd.js"
30
30
  },
31
31
  "./package.json": "./package.json"
32
32
  },
33
33
  "scripts": {
34
34
  "dev": "vite",
35
- "test": "jest",
35
+ "test": "jest --passWithNoTests",
36
36
  "build": "vite build && tsc --emitDeclarationOnly -p tsconfig.prod.json",
37
37
  "clean": "rm -rf dist && find ./src -name '*.js' -type f | xargs rm -f"
38
38
  },
@@ -50,8 +50,8 @@
50
50
  },
51
51
  "devDependencies": {
52
52
  "@jest/globals": "^30.2.0",
53
- "@types/react": "^18.3.24",
54
- "@types/react-dom": "^18.3.7",
53
+ "@types/react": "^19.2.3",
54
+ "@types/react-dom": "^19.2.3",
55
55
  "@vitejs/plugin-react": "^4.7.0",
56
56
  "babel-plugin-react-compiler": "^19.0.0-beta-af1b7da-20250417",
57
57
  "eslint-plugin-react-compiler": "^19.1.0-rc.2",
@@ -69,5 +69,5 @@
69
69
  "publishConfig": {
70
70
  "access": "public"
71
71
  },
72
- "gitHead": "72d951d01d15ef5a7efcde6c63839f65964d2f7a"
72
+ "gitHead": "6281205d9f39f85991e1d8533474bfb9a542ed03"
73
73
  }
@@ -17,6 +17,7 @@ import { CollectionEditorPermissionsBuilder } from "./types/config_permissions";
17
17
  import { CollectionInference } from "./types/collection_inference";
18
18
  import { PropertyFormDialog } from "./ui/collection_editor/PropertyEditView";
19
19
  import { PersistedCollection } from "./types/persisted_collection";
20
+ import { CollectionGenerationCallback } from "./api/generateCollectionApi";
20
21
 
21
22
  export const ConfigControllerContext = React.createContext<CollectionsConfigController>({} as any);
22
23
  export const CollectionEditorContext = React.createContext<CollectionEditorController>({} as any);
@@ -57,21 +58,28 @@ export interface ConfigControllerProviderProps {
57
58
 
58
59
  onAnalyticsEvent?: (event: string, params?: object) => void;
59
60
 
61
+ /**
62
+ * Callback function for generating/modifying collections.
63
+ * The plugin is API-agnostic - the consumer provides the implementation.
64
+ */
65
+ generateCollection?: CollectionGenerationCallback;
66
+
60
67
  }
61
68
 
62
69
  export const ConfigControllerProvider = React.memo(
63
70
  function ConfigControllerProvider({
64
- children,
65
- collectionConfigController,
66
- configPermissions,
67
- reservedGroups,
68
- collectionInference,
69
- extraView,
70
- getUser,
71
- getData,
72
- onAnalyticsEvent,
73
- pathSuggestions
74
- }: PropsWithChildren<ConfigControllerProviderProps>) {
71
+ children,
72
+ collectionConfigController,
73
+ configPermissions,
74
+ reservedGroups,
75
+ collectionInference,
76
+ extraView,
77
+ getUser,
78
+ getData,
79
+ onAnalyticsEvent,
80
+ pathSuggestions,
81
+ generateCollection
82
+ }: PropsWithChildren<ConfigControllerProviderProps>) {
75
83
 
76
84
  const navigation = useNavigationController();
77
85
  const navigate = useNavigate();
@@ -89,9 +97,12 @@ export const ConfigControllerProvider = React.memo(
89
97
  group?: string,
90
98
  name?: string
91
99
  },
100
+ copyFrom?: PersistedCollection,
92
101
  redirect: boolean,
93
102
  existingEntities?: Entity<any>[],
94
103
  pathSuggestions?: string[];
104
+ initialView?: "general" | "display" | "properties";
105
+ expandKanban?: boolean;
95
106
  }>();
96
107
 
97
108
  const [currentPropertyDialog, setCurrentPropertyDialog] = React.useState<{
@@ -104,7 +115,8 @@ export const ConfigControllerProvider = React.memo(
104
115
  fullPath?: string,
105
116
  parentCollectionIds: string[],
106
117
  collectionEditable: boolean;
107
- existingEntities?: Entity<any>[]
118
+ existingEntities?: Entity<any>[];
119
+ collection?: PersistedCollection;
108
120
  }>();
109
121
 
110
122
  const defaultConfigPermissions: CollectionEditorPermissionsBuilder = useCallback(() => ({
@@ -114,17 +126,21 @@ export const ConfigControllerProvider = React.memo(
114
126
  }), []);
115
127
 
116
128
  const editCollection = ({
117
- id,
118
- fullPath,
119
- parentCollectionIds,
120
- parentCollection,
121
- existingEntities
122
- }: {
129
+ id,
130
+ fullPath,
131
+ parentCollectionIds,
132
+ parentCollection,
133
+ existingEntities,
134
+ initialView,
135
+ expandKanban
136
+ }: {
123
137
  id?: string,
124
138
  fullPath?: string,
125
139
  parentCollectionIds: string[],
126
140
  parentCollection?: PersistedCollection,
127
- existingEntities?: Entity<any>[]
141
+ existingEntities?: Entity<any>[],
142
+ initialView?: "general" | "display" | "properties",
143
+ expandKanban?: boolean
128
144
  }) => {
129
145
  console.debug("Edit collection", id, fullPath, parentCollectionIds, parentCollection);
130
146
  onAnalyticsEvent?.("edit_collection", {
@@ -139,19 +155,21 @@ export const ConfigControllerProvider = React.memo(
139
155
  parentCollection,
140
156
  redirect: false,
141
157
  existingEntities,
142
- pathSuggestions
158
+ pathSuggestions,
159
+ initialView,
160
+ expandKanban
143
161
  });
144
162
  };
145
163
 
146
164
  const editProperty = ({
147
- propertyKey,
148
- property,
149
- editedCollectionId,
150
- currentPropertiesOrder,
151
- parentCollectionIds,
152
- collection,
153
- existingEntities
154
- }: {
165
+ propertyKey,
166
+ property,
167
+ editedCollectionId,
168
+ currentPropertiesOrder,
169
+ parentCollectionIds,
170
+ collection,
171
+ existingEntities
172
+ }: {
155
173
  propertyKey?: string,
156
174
  property?: Property,
157
175
  currentPropertiesOrder?: string[],
@@ -180,17 +198,19 @@ export const ConfigControllerProvider = React.memo(
180
198
  editedCollectionId,
181
199
  parentCollectionIds,
182
200
  collectionEditable: collection?.editable === undefined || collection?.editable === true,
183
- existingEntities
201
+ existingEntities,
202
+ collection
184
203
  });
185
204
  };
186
205
 
187
206
  const createCollection = ({
188
- parentCollectionIds,
189
- parentCollection,
190
- initialValues,
191
- redirect,
192
- sourceClick
193
- }: {
207
+ parentCollectionIds,
208
+ parentCollection,
209
+ initialValues,
210
+ copyFrom,
211
+ redirect,
212
+ sourceClick
213
+ }: {
194
214
  parentCollectionIds: string[],
195
215
  parentCollection?: PersistedCollection
196
216
  initialValues?: {
@@ -198,6 +218,7 @@ export const ConfigControllerProvider = React.memo(
198
218
  path?: string,
199
219
  name?: string
200
220
  },
221
+ copyFrom?: PersistedCollection,
201
222
  redirect: boolean,
202
223
  sourceClick?: string
203
224
  }) => {
@@ -205,10 +226,11 @@ export const ConfigControllerProvider = React.memo(
205
226
  parentCollectionIds,
206
227
  parentCollection,
207
228
  initialValues,
229
+ copyFrom,
208
230
  redirect,
209
231
  sourceClick
210
232
  });
211
- onAnalyticsEvent?.("create_collection", {
233
+ onAnalyticsEvent?.(copyFrom ? "duplicate_collection" : "create_collection", {
212
234
  parentCollectionIds,
213
235
  parentCollection,
214
236
  initialValues,
@@ -220,6 +242,7 @@ export const ConfigControllerProvider = React.memo(
220
242
  parentCollectionIds,
221
243
  parentCollection,
222
244
  initialValues,
245
+ copyFrom,
223
246
  redirect,
224
247
  pathSuggestions
225
248
  });
@@ -248,6 +271,8 @@ export const ConfigControllerProvider = React.memo(
248
271
  reservedGroups={reservedGroups}
249
272
  extraView={extraView}
250
273
  getUser={getUser}
274
+ generateCollection={generateCollection}
275
+ onAnalyticsEvent={onAnalyticsEvent}
251
276
  handleClose={(collection) => {
252
277
  if (currentDialog?.redirect) {
253
278
  if (collection && currentDialog?.isNewCollection && !currentDialog.parentCollectionIds.length) {
@@ -256,7 +281,7 @@ export const ConfigControllerProvider = React.memo(
256
281
  }
257
282
  }
258
283
  setCurrentDialog(undefined);
259
- }}/>
284
+ }} />
260
285
 
261
286
  {/* Used for editing properties*/}
262
287
  <PropertyFormDialog
@@ -267,17 +292,27 @@ export const ConfigControllerProvider = React.memo(
267
292
  autoOpenTypeSelect={!currentPropertyDialog ? false : !currentPropertyDialog?.propertyKey}
268
293
  inArray={false}
269
294
  collectionEditable={currentPropertyDialog?.collectionEditable ?? false}
270
- getData={getData && currentPropertyDialog?.editedCollectionId
271
- ? () => {
272
- console.debug("get data for property", currentPropertyDialog?.editedCollectionId);
273
- const resolvedPath = navigation.resolveIdsFrom(currentPropertyDialog.editedCollectionId!)
274
- return getData(resolvedPath, []);
295
+ getData={currentPropertyDialog?.existingEntities || (getData && currentPropertyDialog?.editedCollectionId)
296
+ ? async () => {
297
+ let data: object[] = [];
298
+ // First, use existing entities if available (already loaded in table)
299
+ if (currentPropertyDialog?.existingEntities) {
300
+ data = currentPropertyDialog.existingEntities.map(e => e.values);
301
+ }
302
+ // If getData is available and we have a path, also fetch from database
303
+ if (getData && currentPropertyDialog?.editedCollectionId) {
304
+ console.debug("Get data for property, path:", currentPropertyDialog?.editedCollectionId);
305
+ const resolvedPath = navigation.resolveIdsFrom(currentPropertyDialog.editedCollectionId!);
306
+ const fetchedData = await getData(resolvedPath, []);
307
+ data.push(...fetchedData);
308
+ }
309
+ return data;
275
310
  }
276
311
  : undefined}
277
312
  onPropertyChanged={({
278
- id,
279
- property
280
- }) => {
313
+ id,
314
+ property
315
+ }) => {
281
316
  if (!currentPropertyDialog) return;
282
317
  if (!id) return;
283
318
  const newProperty = !(currentPropertyDialog.propertyKey);
@@ -330,11 +365,11 @@ export const ConfigControllerProvider = React.memo(
330
365
  }}
331
366
  initialErrors={{}}
332
367
  forceShowErrors={false}
333
- existingPropertyKeys={[]}
368
+ existingPropertyKeys={currentPropertyDialog?.collection?.properties ? Object.keys(currentPropertyDialog.collection.properties) : []}
334
369
  allowDataInference={true}
335
370
  propertyConfigs={propertyConfigs}
336
371
  property={currentPropertyDialog?.property}
337
- propertyKey={currentPropertyDialog?.propertyKey}/>
372
+ propertyKey={currentPropertyDialog?.propertyKey} />
338
373
 
339
374
  </CollectionEditorContext.Provider>
340
375
 
@@ -0,0 +1,119 @@
1
+ import { EntityCollection } from "@firecms/core";
2
+
3
+ export interface GenerateCollectionRequest {
4
+ /** User's natural language description of what they want */
5
+ prompt: string;
6
+
7
+ /** Other collections in the project (for context/relationships). Limit to 30. */
8
+ existingCollections: Partial<EntityCollection>[];
9
+
10
+ /** Optional for generate, required for modifications. If provided, modifies this collection */
11
+ existingCollection?: Partial<EntityCollection>;
12
+ }
13
+
14
+ /** Operation types for modifying a collection */
15
+ export type CollectionOperationType = "add" | "modify" | "delete";
16
+
17
+ /** A single operation describing what changed */
18
+ export interface CollectionOperation {
19
+ op: CollectionOperationType;
20
+ path: string;
21
+ value?: any;
22
+ }
23
+
24
+ /** Result from collection generation, including optional delta operations */
25
+ export interface GenerateCollectionResult {
26
+ collection: EntityCollection;
27
+ operations?: CollectionOperation[];
28
+ }
29
+
30
+ /**
31
+ * Callback type for generating or modifying a collection.
32
+ * The plugin is API-agnostic - consumers implement the actual API call.
33
+ */
34
+ export type CollectionGenerationCallback = (
35
+ request: GenerateCollectionRequest
36
+ ) => Promise<GenerateCollectionResult>;
37
+
38
+ export class CollectionGenerationApiError extends Error {
39
+ public code?: string;
40
+
41
+ constructor(message: string, code?: string) {
42
+ super(message);
43
+ this.code = code;
44
+ this.name = "CollectionGenerationApiError";
45
+ }
46
+ }
47
+
48
+ /**
49
+ * Default endpoint for AI collection generation
50
+ */
51
+ export const DEFAULT_COLLECTION_GENERATION_ENDPOINT = "https://api.firecms.co/collections/generate";
52
+
53
+ /**
54
+ * Props for building a collection generation callback
55
+ */
56
+ export interface BuildCollectionGenerationCallbackProps {
57
+ /**
58
+ * Function to get the auth token (e.g., from Firebase Auth)
59
+ * This is typically `authController.getAuthToken` from `@firecms/firebase`
60
+ */
61
+ getAuthToken: () => Promise<string>;
62
+
63
+ /**
64
+ * Optional custom API endpoint for collection generation.
65
+ * Defaults to the FireCMS SaaS API endpoint.
66
+ */
67
+ apiEndpoint?: string;
68
+ }
69
+
70
+ /**
71
+ * Build a callback for AI collection generation.
72
+ * This helper allows self-hosted FireCMS users to enable the AI collection
73
+ * generation feature.
74
+ *
75
+ * @example
76
+ * ```typescript
77
+ * import { useCollectionEditorPlugin, buildCollectionGenerationCallback } from "@firecms/collection_editor";
78
+ * import { useFirebaseAuthController } from "@firecms/firebase";
79
+ *
80
+ * const authController = useFirebaseAuthController({ firebaseApp });
81
+ *
82
+ * const collectionEditorPlugin = useCollectionEditorPlugin({
83
+ * // ... other props
84
+ * generateCollection: buildCollectionGenerationCallback({
85
+ * getAuthToken: authController.getAuthToken
86
+ * })
87
+ * });
88
+ * ```
89
+ */
90
+ export function buildCollectionGenerationCallback({
91
+ getAuthToken,
92
+ apiEndpoint = DEFAULT_COLLECTION_GENERATION_ENDPOINT
93
+ }: BuildCollectionGenerationCallbackProps): CollectionGenerationCallback {
94
+ return async (request) => {
95
+ const token = await getAuthToken();
96
+ const response = await fetch(apiEndpoint, {
97
+ method: "POST",
98
+ headers: {
99
+ "Content-Type": "application/json",
100
+ Authorization: `Bearer ${token}`
101
+ },
102
+ body: JSON.stringify(request)
103
+ });
104
+
105
+ if (!response.ok) {
106
+ const errorData = await response.json().catch(() => ({}));
107
+ throw new CollectionGenerationApiError(
108
+ errorData.error || "Failed to generate collection",
109
+ errorData.code
110
+ );
111
+ }
112
+
113
+ const data = await response.json();
114
+ return {
115
+ collection: data.data.collection,
116
+ operations: data.data.operations
117
+ };
118
+ };
119
+ }
@@ -0,0 +1 @@
1
+ export * from "./generateCollectionApi";
package/src/index.ts CHANGED
@@ -13,9 +13,14 @@ export {
13
13
  editableProperty, removeNonEditableProperties
14
14
  } from "./utils/entities";
15
15
  export * from "./utils/collections";
16
+ export {
17
+ validateCollectionJson,
18
+ type CollectionValidationError,
19
+ type CollectionValidationResult
20
+ } from "./utils/validateCollectionJson";
16
21
 
17
22
  export type {
18
- CollectionsConfigController, DeleteCollectionParams, SaveCollectionParams, UpdateCollectionParams, CollectionsSetupInfo
23
+ CollectionsConfigController, DeleteCollectionParams, SaveCollectionParams, UpdateCollectionParams, CollectionsSetupInfo, UpdatePropertiesOrderParams, UpdateKanbanColumnsOrderParams
19
24
  } from "./types/config_controller";
20
25
  export type {
21
26
  CollectionEditorController
@@ -31,6 +36,28 @@ export type {
31
36
  CollectionInference
32
37
  } from "./types/collection_inference";
33
38
 
39
+ export {
40
+ buildCollectionGenerationCallback,
41
+ CollectionGenerationApiError,
42
+ DEFAULT_COLLECTION_GENERATION_ENDPOINT
43
+ } from "./api/generateCollectionApi";
44
+
45
+ export type {
46
+ CollectionGenerationCallback,
47
+ GenerateCollectionRequest,
48
+ GenerateCollectionResult,
49
+ CollectionOperation,
50
+ CollectionOperationType,
51
+ BuildCollectionGenerationCallbackProps
52
+ } from "./api/generateCollectionApi";
53
+
34
54
  export { MissingReferenceWidget } from "./ui/MissingReferenceWidget";
35
55
 
36
56
  export * from "./ui/collection_editor/util";
57
+
58
+ export {
59
+ PropertyForm,
60
+ PropertyFormDialog,
61
+ type PropertyFormProps,
62
+ type OnPropertyChangedParams
63
+ } from "./ui/collection_editor/PropertyEditView";