@firecms/collection_editor 3.0.0 → 3.1.0-canary.02232f4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ConfigControllerProvider.d.ts +6 -0
- package/dist/api/generateCollectionApi.d.ts +71 -0
- package/dist/api/index.d.ts +1 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.es.js +15234 -8138
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +15199 -8103
- package/dist/index.umd.js.map +1 -1
- package/dist/locales/de.d.ts +120 -0
- package/dist/locales/en.d.ts +120 -0
- package/dist/locales/es.d.ts +120 -0
- package/dist/locales/fr.d.ts +120 -0
- package/dist/locales/hi.d.ts +120 -0
- package/dist/locales/it.d.ts +120 -0
- package/dist/locales/pt.d.ts +120 -0
- package/dist/types/collection_editor_controller.d.ts +14 -0
- package/dist/types/collection_inference.d.ts +8 -2
- package/dist/types/config_controller.d.ts +31 -1
- package/dist/ui/AddKanbanColumnAction.d.ts +11 -0
- package/dist/ui/KanbanSetupAction.d.ts +10 -0
- package/dist/ui/collection_editor/AICollectionGeneratorPopover.d.ts +37 -0
- package/dist/ui/collection_editor/AIModifiedPathsContext.d.ts +20 -0
- package/dist/ui/collection_editor/CollectionDetailsForm.d.ts +2 -3
- package/dist/ui/collection_editor/CollectionEditorDialog.d.ts +24 -0
- package/dist/ui/collection_editor/CollectionEditorWelcomeView.d.ts +4 -1
- package/dist/ui/collection_editor/CollectionJsonImportDialog.d.ts +7 -0
- package/dist/ui/collection_editor/CollectionYupValidation.d.ts +9 -13
- package/dist/ui/collection_editor/DisplaySettingsForm.d.ts +3 -0
- package/dist/ui/collection_editor/EntityActionsEditTab.d.ts +2 -1
- package/dist/ui/collection_editor/ExtendSettingsForm.d.ts +14 -0
- package/dist/ui/collection_editor/GeneralSettingsForm.d.ts +7 -0
- package/dist/ui/collection_editor/KanbanConfigSection.d.ts +4 -0
- package/dist/ui/collection_editor/PropertyEditView.d.ts +6 -1
- package/dist/ui/collection_editor/PropertyTree.d.ts +2 -1
- package/dist/ui/collection_editor/SubcollectionsEditTab.d.ts +2 -1
- package/dist/ui/collection_editor/ViewModeSwitch.d.ts +6 -0
- package/dist/ui/collection_editor/properties/EnumPropertyField.d.ts +2 -1
- package/dist/ui/collection_editor/properties/conditions/ConditionsEditor.d.ts +10 -0
- package/dist/ui/collection_editor/properties/conditions/ConditionsPanel.d.ts +2 -0
- package/dist/ui/collection_editor/properties/conditions/EnumConditionsEditor.d.ts +6 -0
- package/dist/ui/collection_editor/properties/conditions/index.d.ts +6 -0
- package/dist/ui/collection_editor/properties/conditions/property_paths.d.ts +19 -0
- package/dist/useCollectionEditorPlugin.d.ts +7 -1
- package/dist/utils/validateCollectionJson.d.ts +22 -0
- package/package.json +15 -15
- package/src/ConfigControllerProvider.tsx +82 -47
- package/src/api/generateCollectionApi.ts +119 -0
- package/src/api/index.ts +1 -0
- package/src/index.ts +28 -1
- package/src/locales/de.ts +125 -0
- package/src/locales/en.ts +145 -0
- package/src/locales/es.ts +125 -0
- package/src/locales/fr.ts +125 -0
- package/src/locales/hi.ts +125 -0
- package/src/locales/it.ts +125 -0
- package/src/locales/pt.ts +125 -0
- package/src/types/collection_editor_controller.tsx +16 -3
- package/src/types/collection_inference.ts +15 -2
- package/src/types/config_controller.tsx +37 -1
- package/src/ui/AddKanbanColumnAction.tsx +203 -0
- package/src/ui/EditorCollectionAction.tsx +3 -3
- package/src/ui/EditorCollectionActionStart.tsx +1 -2
- package/src/ui/EditorEntityAction.tsx +3 -2
- package/src/ui/HomePageEditorCollectionAction.tsx +41 -13
- package/src/ui/KanbanSetupAction.tsx +38 -0
- package/src/ui/MissingReferenceWidget.tsx +1 -1
- package/src/ui/NewCollectionButton.tsx +4 -2
- package/src/ui/NewCollectionCard.tsx +7 -4
- package/src/ui/PropertyAddColumnComponent.tsx +4 -3
- package/src/ui/collection_editor/AICollectionGeneratorPopover.tsx +243 -0
- package/src/ui/collection_editor/AIModifiedPathsContext.tsx +88 -0
- package/src/ui/collection_editor/CollectionDetailsForm.tsx +222 -268
- package/src/ui/collection_editor/CollectionEditorDialog.tsx +270 -204
- package/src/ui/collection_editor/CollectionEditorWelcomeView.tsx +138 -71
- package/src/ui/collection_editor/CollectionJsonImportDialog.tsx +171 -0
- package/src/ui/collection_editor/CollectionPropertiesEditorForm.tsx +202 -101
- package/src/ui/collection_editor/DisplaySettingsForm.tsx +335 -0
- package/src/ui/collection_editor/EntityActionsEditTab.tsx +106 -97
- package/src/ui/collection_editor/EntityActionsSelectDialog.tsx +8 -10
- package/src/ui/collection_editor/EntityCustomViewsSelectDialog.tsx +5 -7
- package/src/ui/collection_editor/EnumForm.tsx +153 -102
- package/src/ui/collection_editor/ExtendSettingsForm.tsx +94 -0
- package/src/ui/collection_editor/GeneralSettingsForm.tsx +335 -0
- package/src/ui/collection_editor/GetCodeDialog.tsx +63 -41
- package/src/ui/collection_editor/KanbanConfigSection.tsx +209 -0
- package/src/ui/collection_editor/LayoutModeSwitch.tsx +27 -43
- package/src/ui/collection_editor/PropertyEditView.tsx +272 -199
- package/src/ui/collection_editor/PropertyFieldPreview.tsx +1 -1
- package/src/ui/collection_editor/PropertyTree.tsx +130 -58
- package/src/ui/collection_editor/SubcollectionsEditTab.tsx +169 -163
- package/src/ui/collection_editor/UnsavedChangesDialog.tsx +0 -2
- package/src/ui/collection_editor/ViewModeSwitch.tsx +43 -0
- package/src/ui/collection_editor/import/CollectionEditorImportDataPreview.tsx +6 -3
- package/src/ui/collection_editor/import/CollectionEditorImportMapping.tsx +5 -2
- package/src/ui/collection_editor/properties/BlockPropertyField.tsx +0 -2
- package/src/ui/collection_editor/properties/BooleanPropertyField.tsx +4 -1
- package/src/ui/collection_editor/properties/CommonPropertyFields.tsx +6 -4
- package/src/ui/collection_editor/properties/DateTimePropertyField.tsx +126 -42
- package/src/ui/collection_editor/properties/EnumPropertyField.tsx +32 -24
- package/src/ui/collection_editor/properties/MapPropertyField.tsx +8 -9
- package/src/ui/collection_editor/properties/MarkdownPropertyField.tsx +128 -53
- package/src/ui/collection_editor/properties/NumberPropertyField.tsx +3 -1
- package/src/ui/collection_editor/properties/ReferencePropertyField.tsx +6 -9
- package/src/ui/collection_editor/properties/StoragePropertyField.tsx +65 -49
- package/src/ui/collection_editor/properties/StringPropertyField.tsx +3 -1
- package/src/ui/collection_editor/properties/UrlPropertyField.tsx +12 -10
- package/src/ui/collection_editor/properties/advanced/AdvancedPropertyValidation.tsx +23 -4
- package/src/ui/collection_editor/properties/conditions/ConditionsEditor.tsx +866 -0
- package/src/ui/collection_editor/properties/conditions/ConditionsPanel.tsx +28 -0
- package/src/ui/collection_editor/properties/conditions/EnumConditionsEditor.tsx +599 -0
- package/src/ui/collection_editor/properties/conditions/index.ts +6 -0
- package/src/ui/collection_editor/properties/conditions/property_paths.ts +92 -0
- package/src/ui/collection_editor/properties/validation/ArrayPropertyValidation.tsx +5 -2
- package/src/ui/collection_editor/properties/validation/GeneralPropertyValidation.tsx +7 -5
- package/src/ui/collection_editor/properties/validation/NumberPropertyValidation.tsx +10 -7
- package/src/ui/collection_editor/properties/validation/StringPropertyValidation.tsx +11 -9
- package/src/ui/collection_editor/properties/validation/ValidationPanel.tsx +5 -2
- package/src/useCollectionEditorPlugin.tsx +53 -22
- package/src/utils/validateCollectionJson.ts +380 -0
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Properties } from "@firecms/core";
|
|
2
|
+
export interface ConditionsEditorProps {
|
|
3
|
+
disabled: boolean;
|
|
4
|
+
/**
|
|
5
|
+
* Optional collection properties for populating the field selector.
|
|
6
|
+
* If not provided, a basic set of common fields is used.
|
|
7
|
+
*/
|
|
8
|
+
collectionProperties?: Properties;
|
|
9
|
+
}
|
|
10
|
+
export declare function ConditionsEditor({ disabled, collectionProperties }: ConditionsEditorProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -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.
|
|
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.
|
|
11
|
-
"@firecms/data_import": "^3.0.
|
|
12
|
-
"@firecms/data_import_export": "^3.0.
|
|
13
|
-
"@firecms/formex": "^3.0.
|
|
14
|
-
"@firecms/schema_inference": "^3.0.
|
|
15
|
-
"@firecms/ui": "^3.0.
|
|
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": "^
|
|
54
|
-
"@types/react-dom": "^
|
|
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": "
|
|
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
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
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
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
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
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
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
|
-
|
|
273
|
-
|
|
274
|
-
|
|
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
|
-
|
|
279
|
-
|
|
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
|
+
}
|
package/src/api/index.ts
ADDED
|
@@ -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
|
|
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";
|