@firecms/collection_editor 3.0.0-alpha.9 → 3.0.0-beta.2-pre.1
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 +3 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.es.js +3128 -4094
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +3 -1
- package/dist/index.umd.js.map +1 -1
- package/dist/types/collection_editor_controller.d.ts +22 -7
- package/dist/types/collection_inference.d.ts +1 -1
- package/dist/types/config_controller.d.ts +32 -5
- package/dist/types/persisted_collection.d.ts +3 -1
- package/dist/ui/CollectionViewHeaderAction.d.ts +10 -0
- package/dist/{components → ui}/EditorCollectionAction.d.ts +1 -1
- package/dist/ui/MissingReferenceWidget.d.ts +3 -0
- package/dist/ui/NewCollectionButton.d.ts +1 -0
- package/dist/ui/PropertyAddColumnComponent.d.ts +6 -0
- package/dist/ui/RootCollectionSuggestions.d.ts +1 -0
- package/dist/{components → ui}/collection_editor/CollectionDetailsForm.d.ts +3 -2
- package/dist/{components → ui}/collection_editor/CollectionEditorDialog.d.ts +11 -6
- package/dist/{components → ui}/collection_editor/CollectionPropertiesEditorForm.d.ts +6 -3
- package/dist/{components → ui}/collection_editor/CollectionYupValidation.d.ts +3 -0
- package/dist/ui/collection_editor/EntityCustomViewsSelectDialog.d.ts +4 -0
- package/dist/ui/collection_editor/GetCodeDialog.d.ts +5 -0
- package/dist/{components → ui}/collection_editor/PropertyEditView.d.ts +8 -6
- package/dist/{components → ui}/collection_editor/PropertyFieldPreview.d.ts +4 -3
- package/dist/ui/collection_editor/PropertySelectItem.d.ts +8 -0
- package/dist/{components → ui}/collection_editor/PropertyTree.d.ts +8 -5
- package/dist/{components → ui}/collection_editor/SubcollectionsEditTab.d.ts +2 -2
- package/dist/{components → ui}/collection_editor/import/CollectionEditorImportDataPreview.d.ts +1 -1
- package/dist/ui/collection_editor/import/CollectionEditorImportMapping.d.ts +7 -0
- package/dist/{components → ui}/collection_editor/import/clean_import_data.d.ts +1 -1
- package/dist/{components → ui}/collection_editor/properties/BlockPropertyField.d.ts +4 -1
- package/dist/{components → ui}/collection_editor/properties/CommonPropertyFields.d.ts +1 -0
- package/dist/{components → ui}/collection_editor/properties/MapPropertyField.d.ts +4 -1
- package/dist/{components → ui}/collection_editor/properties/RepeatPropertyField.d.ts +4 -1
- package/dist/{components → ui}/collection_editor/properties/StringPropertyField.d.ts +1 -1
- package/dist/ui/collection_editor/properties/UrlPropertyField.d.ts +4 -0
- package/dist/ui/collection_editor/templates/blog_template.d.ts +2 -0
- package/dist/ui/collection_editor/templates/pages_template.d.ts +2 -0
- package/dist/ui/collection_editor/templates/products_template.d.ts +2 -0
- package/dist/ui/collection_editor/templates/users_template.d.ts +2 -0
- package/dist/ui/collection_editor/utils/strings.d.ts +1 -0
- package/dist/ui/collection_editor/utils/supported_fields.d.ts +3 -0
- package/dist/ui/collection_editor/utils/update_property_for_widget.d.ts +2 -0
- package/dist/useCollectionEditorPlugin.d.ts +5 -3
- package/dist/utils/entities.d.ts +3 -4
- package/package.json +22 -19
- package/src/ConfigControllerProvider.tsx +336 -0
- package/src/index.ts +35 -0
- package/src/types/collection_editor_controller.tsx +42 -0
- package/src/types/collection_inference.ts +3 -0
- package/src/types/config_controller.tsx +60 -0
- package/src/types/config_permissions.ts +20 -0
- package/src/types/persisted_collection.ts +9 -0
- package/src/ui/CollectionViewHeaderAction.tsx +43 -0
- package/src/ui/EditorCollectionAction.tsx +109 -0
- package/src/ui/HomePageEditorCollectionAction.tsx +84 -0
- package/src/ui/MissingReferenceWidget.tsx +35 -0
- package/src/ui/NewCollectionButton.tsx +16 -0
- package/src/ui/NewCollectionCard.tsx +47 -0
- package/src/ui/PropertyAddColumnComponent.tsx +42 -0
- package/src/ui/RootCollectionSuggestions.tsx +55 -0
- package/src/ui/collection_editor/CollectionDetailsForm.tsx +366 -0
- package/src/ui/collection_editor/CollectionEditorDialog.tsx +754 -0
- package/src/ui/collection_editor/CollectionEditorWelcomeView.tsx +206 -0
- package/src/ui/collection_editor/CollectionPropertiesEditorForm.tsx +481 -0
- package/src/ui/collection_editor/CollectionYupValidation.tsx +7 -0
- package/src/ui/collection_editor/EntityCustomViewsSelectDialog.tsx +37 -0
- package/src/ui/collection_editor/EnumForm.tsx +354 -0
- package/src/ui/collection_editor/GetCodeDialog.tsx +110 -0
- package/src/ui/collection_editor/PropertyEditView.tsx +558 -0
- package/src/ui/collection_editor/PropertyFieldPreview.tsx +203 -0
- package/src/ui/collection_editor/PropertySelectItem.tsx +32 -0
- package/src/ui/collection_editor/PropertyTree.tsx +233 -0
- package/src/ui/collection_editor/SubcollectionsEditTab.tsx +253 -0
- package/src/ui/collection_editor/UnsavedChangesDialog.tsx +47 -0
- package/src/ui/collection_editor/import/CollectionEditorImportDataPreview.tsx +37 -0
- package/src/ui/collection_editor/import/CollectionEditorImportMapping.tsx +260 -0
- package/src/ui/collection_editor/import/clean_import_data.ts +53 -0
- package/src/ui/collection_editor/properties/BlockPropertyField.tsx +135 -0
- package/src/ui/collection_editor/properties/BooleanPropertyField.tsx +36 -0
- package/src/ui/collection_editor/properties/CommonPropertyFields.tsx +137 -0
- package/src/ui/collection_editor/properties/DateTimePropertyField.tsx +87 -0
- package/src/ui/collection_editor/properties/EnumPropertyField.tsx +117 -0
- package/src/ui/collection_editor/properties/FieldHelperView.tsx +13 -0
- package/src/ui/collection_editor/properties/KeyValuePropertyField.tsx +20 -0
- package/src/ui/collection_editor/properties/MapPropertyField.tsx +149 -0
- package/src/ui/collection_editor/properties/NumberPropertyField.tsx +38 -0
- package/src/ui/collection_editor/properties/ReferencePropertyField.tsx +165 -0
- package/src/ui/collection_editor/properties/RepeatPropertyField.tsx +108 -0
- package/src/ui/collection_editor/properties/StoragePropertyField.tsx +194 -0
- package/src/ui/collection_editor/properties/StringPropertyField.tsx +79 -0
- package/src/ui/collection_editor/properties/UrlPropertyField.tsx +89 -0
- package/src/ui/collection_editor/properties/advanced/AdvancedPropertyValidation.tsx +36 -0
- package/src/ui/collection_editor/properties/validation/ArrayPropertyValidation.tsx +50 -0
- package/src/ui/collection_editor/properties/validation/GeneralPropertyValidation.tsx +50 -0
- package/src/ui/collection_editor/properties/validation/NumberPropertyValidation.tsx +100 -0
- package/src/ui/collection_editor/properties/validation/StringPropertyValidation.tsx +132 -0
- package/src/ui/collection_editor/properties/validation/ValidationPanel.tsx +28 -0
- package/src/ui/collection_editor/templates/blog_template.ts +115 -0
- package/src/ui/collection_editor/templates/pages_template.ts +188 -0
- package/src/ui/collection_editor/templates/products_template.ts +88 -0
- package/src/ui/collection_editor/templates/users_template.ts +42 -0
- package/src/ui/collection_editor/util.ts +21 -0
- package/src/ui/collection_editor/utils/strings.ts +8 -0
- package/src/ui/collection_editor/utils/supported_fields.tsx +29 -0
- package/src/ui/collection_editor/utils/update_property_for_widget.ts +271 -0
- package/src/ui/collection_editor/utils/useTraceUpdate.tsx +23 -0
- package/src/useCollectionEditorController.tsx +9 -0
- package/src/useCollectionEditorPlugin.tsx +137 -0
- package/src/useCollectionsConfigController.tsx +9 -0
- package/src/utils/arrays.ts +3 -0
- package/src/utils/entities.ts +38 -0
- package/src/vite-env.d.ts +1 -0
- package/dist/components/collection_editor/PropertySelectItem.d.ts +0 -8
- package/dist/components/collection_editor/SelectIcons.d.ts +0 -6
- package/dist/components/collection_editor/import/CollectionEditorImportMapping.d.ts +0 -4
- package/dist/components/collection_editor/templates/blog_template.d.ts +0 -10
- package/dist/components/collection_editor/templates/products_template.d.ts +0 -12
- package/dist/components/collection_editor/templates/users_template.d.ts +0 -7
- package/dist/components/collection_editor/utils/supported_fields.d.ts +0 -3
- package/dist/components/collection_editor/utils/update_property_for_widget.d.ts +0 -3
- package/dist/types/editable_properties.d.ts +0 -10
- package/dist/utils/icons.d.ts +0 -2
- package/dist/utils/synonyms.d.ts +0 -1951
- /package/dist/{components → ui}/HomePageEditorCollectionAction.d.ts +0 -0
- /package/dist/{components → ui}/NewCollectionCard.d.ts +0 -0
- /package/dist/{components → ui}/collection_editor/CollectionEditorWelcomeView.d.ts +0 -0
- /package/dist/{components → ui}/collection_editor/EnumForm.d.ts +0 -0
- /package/dist/{components → ui}/collection_editor/UnsavedChangesDialog.d.ts +0 -0
- /package/dist/{components → ui}/collection_editor/properties/BooleanPropertyField.d.ts +0 -0
- /package/dist/{components → ui}/collection_editor/properties/DateTimePropertyField.d.ts +0 -0
- /package/dist/{components → ui}/collection_editor/properties/EnumPropertyField.d.ts +0 -0
- /package/dist/{components → ui}/collection_editor/properties/FieldHelperView.d.ts +0 -0
- /package/dist/{components → ui}/collection_editor/properties/KeyValuePropertyField.d.ts +0 -0
- /package/dist/{components → ui}/collection_editor/properties/NumberPropertyField.d.ts +0 -0
- /package/dist/{components → ui}/collection_editor/properties/ReferencePropertyField.d.ts +0 -0
- /package/dist/{components → ui}/collection_editor/properties/StoragePropertyField.d.ts +0 -0
- /package/dist/{components → ui}/collection_editor/properties/advanced/AdvancedPropertyValidation.d.ts +0 -0
- /package/dist/{components → ui}/collection_editor/properties/validation/ArrayPropertyValidation.d.ts +0 -0
- /package/dist/{components → ui}/collection_editor/properties/validation/GeneralPropertyValidation.d.ts +0 -0
- /package/dist/{components → ui}/collection_editor/properties/validation/NumberPropertyValidation.d.ts +0 -0
- /package/dist/{components → ui}/collection_editor/properties/validation/StringPropertyValidation.d.ts +0 -0
- /package/dist/{components → ui}/collection_editor/properties/validation/ValidationPanel.d.ts +0 -0
- /package/dist/{components → ui}/collection_editor/util.d.ts +0 -0
- /package/dist/{components → ui}/collection_editor/utils/useTraceUpdate.d.ts +0 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DeleteConfirmationDialog,
|
|
3
|
+
PluginHomePageActionsProps,
|
|
4
|
+
useAuthController,
|
|
5
|
+
useSnackbarController
|
|
6
|
+
} from "@firecms/core";
|
|
7
|
+
import { DeleteIcon, IconButton, Menu, MenuItem, MoreVertIcon, SettingsIcon, } from "@firecms/ui";
|
|
8
|
+
import { useCollectionEditorController } from "../useCollectionEditorController";
|
|
9
|
+
import { useCallback, useState } from "react";
|
|
10
|
+
import { useCollectionsConfigController } from "../useCollectionsConfigController";
|
|
11
|
+
|
|
12
|
+
export function HomePageEditorCollectionAction({
|
|
13
|
+
path,
|
|
14
|
+
collection
|
|
15
|
+
}: PluginHomePageActionsProps) {
|
|
16
|
+
|
|
17
|
+
const snackbarController = useSnackbarController();
|
|
18
|
+
const authController = useAuthController();
|
|
19
|
+
const configController = useCollectionsConfigController();
|
|
20
|
+
const collectionEditorController = useCollectionEditorController();
|
|
21
|
+
|
|
22
|
+
const permissions = collectionEditorController.configPermissions({
|
|
23
|
+
user: authController.user,
|
|
24
|
+
collection
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const onEditCollectionClicked = useCallback(() => {
|
|
28
|
+
collectionEditorController?.editCollection({ path, parentCollectionIds: [] });
|
|
29
|
+
}, [collectionEditorController, path]);
|
|
30
|
+
|
|
31
|
+
const [deleteRequested, setDeleteRequested] = useState(false);
|
|
32
|
+
|
|
33
|
+
const deleteCollection = useCallback(() => {
|
|
34
|
+
configController?.deleteCollection({ path }).then(() => {
|
|
35
|
+
setDeleteRequested(false);
|
|
36
|
+
snackbarController.open({
|
|
37
|
+
message: "Collection deleted",
|
|
38
|
+
type: "success"
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
}, [path, configController]);
|
|
42
|
+
|
|
43
|
+
return <>
|
|
44
|
+
|
|
45
|
+
<div>
|
|
46
|
+
{permissions.deleteCollections &&
|
|
47
|
+
<Menu
|
|
48
|
+
trigger={<IconButton>
|
|
49
|
+
<MoreVertIcon size={"small"}/>
|
|
50
|
+
</IconButton>}
|
|
51
|
+
>
|
|
52
|
+
<MenuItem onClick={(event) => {
|
|
53
|
+
event.preventDefault();
|
|
54
|
+
event.stopPropagation();
|
|
55
|
+
setDeleteRequested(true);
|
|
56
|
+
}}>
|
|
57
|
+
<DeleteIcon/>
|
|
58
|
+
Delete
|
|
59
|
+
</MenuItem>
|
|
60
|
+
|
|
61
|
+
</Menu>
|
|
62
|
+
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
{permissions.editCollections &&
|
|
66
|
+
<IconButton
|
|
67
|
+
onClick={(event) => {
|
|
68
|
+
onEditCollectionClicked();
|
|
69
|
+
}}>
|
|
70
|
+
<SettingsIcon size={"small"}/>
|
|
71
|
+
</IconButton>}
|
|
72
|
+
</div>
|
|
73
|
+
|
|
74
|
+
<DeleteConfirmationDialog
|
|
75
|
+
open={deleteRequested}
|
|
76
|
+
onAccept={deleteCollection}
|
|
77
|
+
onCancel={() => setDeleteRequested(false)}
|
|
78
|
+
title={<>Delete this collection?</>}
|
|
79
|
+
body={<> This will <b>not
|
|
80
|
+
delete any data</b>, only
|
|
81
|
+
the collection in the CMS</>}/>
|
|
82
|
+
</>;
|
|
83
|
+
|
|
84
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { ErrorView, unslugify, useNavigationController } from "@firecms/core";
|
|
2
|
+
import { useCollectionEditorController } from "../useCollectionEditorController";
|
|
3
|
+
import { Button } from "@firecms/ui";
|
|
4
|
+
|
|
5
|
+
export function MissingReferenceWidget({ path: pathProp }: {
|
|
6
|
+
path: string
|
|
7
|
+
}) {
|
|
8
|
+
const navigation = useNavigationController();
|
|
9
|
+
const path = getLastSegment(pathProp);
|
|
10
|
+
const parentCollectionIds = navigation.getParentCollectionIds(pathProp);
|
|
11
|
+
const collectionEditor = useCollectionEditorController();
|
|
12
|
+
return <div className={"p-1 flex flex-col items-center"}>
|
|
13
|
+
<ErrorView error={"No collection for path: " + path}/>
|
|
14
|
+
<Button className={"mx-2"} variant={"outlined"} size={"small"}
|
|
15
|
+
onClick={() => {
|
|
16
|
+
collectionEditor.createCollection({
|
|
17
|
+
initialValues: { path, name: unslugify(path) },
|
|
18
|
+
parentCollectionIds,
|
|
19
|
+
redirect: false
|
|
20
|
+
});
|
|
21
|
+
}}>
|
|
22
|
+
Create
|
|
23
|
+
</Button>
|
|
24
|
+
</div>;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function getParentPathSegments(path: string): string[] {
|
|
28
|
+
const segments = path.split("/");
|
|
29
|
+
return segments.filter((segment, index) => index % 2 === 0 && index !== segments.length - 1);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function getLastSegment(path: string): string {
|
|
33
|
+
const segments = path.split("/");
|
|
34
|
+
return segments[segments.length - 1];
|
|
35
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { AddIcon, Button } from "@firecms/ui";
|
|
2
|
+
import { useCollectionEditorController } from "../useCollectionEditorController";
|
|
3
|
+
|
|
4
|
+
export function NewCollectionButton() {
|
|
5
|
+
const collectionEditorController = useCollectionEditorController();
|
|
6
|
+
return <Button className={"min-w-fit"}
|
|
7
|
+
variant={"outlined"}
|
|
8
|
+
onClick={() => collectionEditorController.createCollection({
|
|
9
|
+
parentCollectionIds: [],
|
|
10
|
+
redirect: true
|
|
11
|
+
})
|
|
12
|
+
}>
|
|
13
|
+
<AddIcon/>
|
|
14
|
+
New collection
|
|
15
|
+
</Button>
|
|
16
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { PluginHomePageAdditionalCardsProps, useAuthController } from "@firecms/core";
|
|
2
|
+
import { AddIcon, Card, cn, Typography } from "@firecms/ui";
|
|
3
|
+
import { useCollectionEditorController } from "../useCollectionEditorController";
|
|
4
|
+
|
|
5
|
+
export function NewCollectionCard({
|
|
6
|
+
group,
|
|
7
|
+
context
|
|
8
|
+
}: PluginHomePageAdditionalCardsProps) {
|
|
9
|
+
|
|
10
|
+
if (!context.navigation.topLevelNavigation)
|
|
11
|
+
throw Error("Navigation not ready in FireCMSHomePage");
|
|
12
|
+
|
|
13
|
+
const authController = useAuthController();
|
|
14
|
+
|
|
15
|
+
const collectionEditorController = useCollectionEditorController();
|
|
16
|
+
const canCreateCollections = collectionEditorController.configPermissions
|
|
17
|
+
? collectionEditorController.configPermissions({
|
|
18
|
+
user: authController.user,
|
|
19
|
+
}).createCollections
|
|
20
|
+
: true;
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<Card className={cn("h-full p-4 min-h-[124px]")}
|
|
24
|
+
onClick={collectionEditorController && canCreateCollections
|
|
25
|
+
? () => collectionEditorController.createCollection({
|
|
26
|
+
initialValues: group ? { group } : undefined,
|
|
27
|
+
parentCollectionIds: [],
|
|
28
|
+
redirect: true
|
|
29
|
+
})
|
|
30
|
+
: undefined}>
|
|
31
|
+
|
|
32
|
+
<div
|
|
33
|
+
className="flex flex-col items-start h-full w-full items-center justify-center h-full w-full flex-grow flex-col">
|
|
34
|
+
<AddIcon color="primary" size={"large"}/>
|
|
35
|
+
<Typography color="primary"
|
|
36
|
+
variant={"caption"}
|
|
37
|
+
className={"font-medium"}>{"Add new collection".toUpperCase()}</Typography>
|
|
38
|
+
|
|
39
|
+
{!canCreateCollections &&
|
|
40
|
+
<Typography variant={"caption"}>You don't have permissions to create
|
|
41
|
+
collections</Typography>
|
|
42
|
+
}
|
|
43
|
+
</div>
|
|
44
|
+
|
|
45
|
+
</Card>
|
|
46
|
+
);
|
|
47
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { getDefaultPropertiesOrder, useAuthController } from "@firecms/core";
|
|
2
|
+
import { AddIcon, Tooltip } from "@firecms/ui";
|
|
3
|
+
import { useCollectionEditorController } from "../useCollectionEditorController";
|
|
4
|
+
import { PersistedCollection } from "../types/persisted_collection";
|
|
5
|
+
|
|
6
|
+
export function PropertyAddColumnComponent({
|
|
7
|
+
fullPath,
|
|
8
|
+
parentCollectionIds,
|
|
9
|
+
collection
|
|
10
|
+
}: {
|
|
11
|
+
fullPath: string,
|
|
12
|
+
parentCollectionIds: string[],
|
|
13
|
+
collection: PersistedCollection;
|
|
14
|
+
}) {
|
|
15
|
+
|
|
16
|
+
const authController = useAuthController();
|
|
17
|
+
const collectionEditorController = useCollectionEditorController();
|
|
18
|
+
const canEditCollection = collectionEditorController.configPermissions
|
|
19
|
+
? collectionEditorController.configPermissions({
|
|
20
|
+
user: authController.user,
|
|
21
|
+
collection
|
|
22
|
+
}).editCollections
|
|
23
|
+
: true;
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<Tooltip title={canEditCollection ? "Add new property" : "You don't have permission to add new properties"}>
|
|
27
|
+
<div
|
|
28
|
+
className={"p-0.5 w-20 h-full flex items-center justify-center cursor-pointer bg-gray-100 bg-opacity-40 hover:bg-gray-100 dark:bg-gray-950 dark:bg-opacity-40 dark:hover:bg-gray-950"}
|
|
29
|
+
// className={onHover ? "bg-white dark:bg-gray-950" : undefined}
|
|
30
|
+
onClick={() => {
|
|
31
|
+
collectionEditorController.editProperty({
|
|
32
|
+
editedCollectionPath: fullPath,
|
|
33
|
+
parentCollectionIds,
|
|
34
|
+
currentPropertiesOrder: getDefaultPropertiesOrder(collection),
|
|
35
|
+
collection
|
|
36
|
+
});
|
|
37
|
+
}}>
|
|
38
|
+
<AddIcon color={"inherit"}/>
|
|
39
|
+
</div>
|
|
40
|
+
</Tooltip>
|
|
41
|
+
)
|
|
42
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { unslugify, useAuthController, useNavigationController } from "@firecms/core";
|
|
2
|
+
import { AddIcon, Chip, Collapse, Typography, } from "@firecms/ui";
|
|
3
|
+
import { useCollectionEditorController } from "../useCollectionEditorController";
|
|
4
|
+
import React from "react";
|
|
5
|
+
|
|
6
|
+
export function RootCollectionSuggestions() {
|
|
7
|
+
|
|
8
|
+
const authController = useAuthController();
|
|
9
|
+
const navigationController = useNavigationController();
|
|
10
|
+
|
|
11
|
+
const collectionEditorController = useCollectionEditorController();
|
|
12
|
+
const canCreateCollections = collectionEditorController.configPermissions
|
|
13
|
+
? collectionEditorController.configPermissions({
|
|
14
|
+
user: authController.user
|
|
15
|
+
}).createCollections
|
|
16
|
+
: true;
|
|
17
|
+
|
|
18
|
+
const rootPathSuggestions = collectionEditorController.rootPathSuggestions ?? [];
|
|
19
|
+
|
|
20
|
+
const showSuggestions = rootPathSuggestions.length > 3 || (navigationController.collections.length === 0 && rootPathSuggestions.length > 0);
|
|
21
|
+
return <Collapse
|
|
22
|
+
in={showSuggestions}>
|
|
23
|
+
|
|
24
|
+
<div
|
|
25
|
+
className={"flex flex-col gap-1 p-2 my-4"}>
|
|
26
|
+
|
|
27
|
+
<Typography variant={"body2"} color={"secondary"}>
|
|
28
|
+
Create a collection from your data:
|
|
29
|
+
</Typography>
|
|
30
|
+
|
|
31
|
+
<div
|
|
32
|
+
className={"flex flex-row gap-1 overflow-scroll no-scrollbar "}>
|
|
33
|
+
{rootPathSuggestions.map((path) => {
|
|
34
|
+
return (
|
|
35
|
+
<div key={path}>
|
|
36
|
+
<Chip
|
|
37
|
+
icon={<AddIcon size={"small"}/>}
|
|
38
|
+
colorScheme={"cyanLighter"}
|
|
39
|
+
onClick={collectionEditorController && canCreateCollections
|
|
40
|
+
? () => collectionEditorController.createCollection({
|
|
41
|
+
initialValues: { path, name: unslugify(path) },
|
|
42
|
+
parentCollectionIds: [],
|
|
43
|
+
redirect: true
|
|
44
|
+
})
|
|
45
|
+
: undefined}
|
|
46
|
+
size="small">
|
|
47
|
+
{path}
|
|
48
|
+
</Chip>
|
|
49
|
+
</div>
|
|
50
|
+
);
|
|
51
|
+
})}
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
</Collapse>
|
|
55
|
+
}
|
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
import React, { useEffect, useState } from "react";
|
|
2
|
+
import { EntityCollection, IconForView, SearchIconsView, singular, toSnakeCase, } from "@firecms/core";
|
|
3
|
+
import {
|
|
4
|
+
Autocomplete,
|
|
5
|
+
AutocompleteItem,
|
|
6
|
+
BooleanSwitchWithLabel,
|
|
7
|
+
Chip,
|
|
8
|
+
cn,
|
|
9
|
+
Container,
|
|
10
|
+
DebouncedTextField,
|
|
11
|
+
Dialog,
|
|
12
|
+
ExpandablePanel,
|
|
13
|
+
IconButton,
|
|
14
|
+
Select,
|
|
15
|
+
SelectItem,
|
|
16
|
+
SettingsIcon,
|
|
17
|
+
TextField,
|
|
18
|
+
Tooltip,
|
|
19
|
+
Typography,
|
|
20
|
+
useAutoComplete
|
|
21
|
+
} from "@firecms/ui";
|
|
22
|
+
import { Field, getIn, useFormikContext } from "formik";
|
|
23
|
+
|
|
24
|
+
import { FieldHelperView } from "./properties/FieldHelperView";
|
|
25
|
+
|
|
26
|
+
export function CollectionDetailsForm({
|
|
27
|
+
isNewCollection,
|
|
28
|
+
reservedGroups,
|
|
29
|
+
existingPaths,
|
|
30
|
+
existingIds,
|
|
31
|
+
groups,
|
|
32
|
+
parentCollection
|
|
33
|
+
}: {
|
|
34
|
+
isNewCollection: boolean,
|
|
35
|
+
reservedGroups?: string[];
|
|
36
|
+
existingPaths?: string[];
|
|
37
|
+
existingIds?: string[];
|
|
38
|
+
groups: string[] | null;
|
|
39
|
+
parentCollection?: EntityCollection;
|
|
40
|
+
parentCollectionIds?: string[];
|
|
41
|
+
}) {
|
|
42
|
+
|
|
43
|
+
const groupRef = React.useRef<HTMLInputElement>(null);
|
|
44
|
+
const {
|
|
45
|
+
values,
|
|
46
|
+
setFieldValue,
|
|
47
|
+
handleChange,
|
|
48
|
+
touched,
|
|
49
|
+
errors,
|
|
50
|
+
setFieldTouched,
|
|
51
|
+
isSubmitting,
|
|
52
|
+
submitCount
|
|
53
|
+
} = useFormikContext<EntityCollection>();
|
|
54
|
+
|
|
55
|
+
const [iconDialogOpen, setIconDialogOpen] = useState(false);
|
|
56
|
+
const [advancedPanelExpanded, setAdvancedPanelExpanded] = useState(false);
|
|
57
|
+
|
|
58
|
+
const updateName = (name: string) => {
|
|
59
|
+
setFieldValue("name", name);
|
|
60
|
+
|
|
61
|
+
const pathTouched = getIn(touched, "path");
|
|
62
|
+
if (!pathTouched && isNewCollection && name) {
|
|
63
|
+
setFieldValue("path", toSnakeCase(name));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const idTouched = getIn(touched, "id");
|
|
67
|
+
if (!idTouched && isNewCollection && name) {
|
|
68
|
+
setFieldValue("id", toSnakeCase(name));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const singularNameTouched = getIn(touched, "singularName");
|
|
72
|
+
if (!singularNameTouched && isNewCollection && name) {
|
|
73
|
+
setFieldValue("singularName", singular(name))
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
useEffect(() => {
|
|
79
|
+
if (errors.id) {
|
|
80
|
+
setAdvancedPanelExpanded(true);
|
|
81
|
+
}
|
|
82
|
+
}, [errors.id]);
|
|
83
|
+
|
|
84
|
+
const collectionIcon = <IconForView collectionOrView={values}/>;
|
|
85
|
+
|
|
86
|
+
const groupOptions = groups?.filter((group) => !reservedGroups?.includes(group));
|
|
87
|
+
|
|
88
|
+
const {
|
|
89
|
+
inputFocused,
|
|
90
|
+
autoCompleteOpen,
|
|
91
|
+
setAutoCompleteOpen
|
|
92
|
+
} = useAutoComplete({
|
|
93
|
+
ref: groupRef
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
const isSubcollection = !!parentCollection;
|
|
97
|
+
|
|
98
|
+
let customIdValue: "true" | "false" | "optional" | "code_defined" | undefined;
|
|
99
|
+
if (customIdValue) {
|
|
100
|
+
if (typeof values.customId === "object") {
|
|
101
|
+
customIdValue = "code_defined";
|
|
102
|
+
} else if (values.customId === true) {
|
|
103
|
+
customIdValue = "true";
|
|
104
|
+
} else if (values.customId === false) {
|
|
105
|
+
customIdValue = "false";
|
|
106
|
+
} else if (values.customId === "optional") {
|
|
107
|
+
customIdValue = "optional";
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return (
|
|
111
|
+
<div className={"overflow-auto my-auto"}>
|
|
112
|
+
<Container maxWidth={"4xl"} className={"flex flex-col gap-4 p-8 m-auto"}>
|
|
113
|
+
|
|
114
|
+
<div>
|
|
115
|
+
<div
|
|
116
|
+
className="flex flex-row py-2 pt-3 items-center">
|
|
117
|
+
<Typography variant={!isNewCollection ? "h5" : "h4"} className={"flex-grow"}>
|
|
118
|
+
{isNewCollection ? "New collection" : `${values?.name} collection`}
|
|
119
|
+
</Typography>
|
|
120
|
+
<Tooltip title={"Change icon"}>
|
|
121
|
+
<IconButton
|
|
122
|
+
shape={"square"}
|
|
123
|
+
onClick={() => setIconDialogOpen(true)}>
|
|
124
|
+
{collectionIcon}
|
|
125
|
+
</IconButton>
|
|
126
|
+
</Tooltip>
|
|
127
|
+
</div>
|
|
128
|
+
|
|
129
|
+
{parentCollection && <Chip colorScheme={"tealDarker"}>
|
|
130
|
+
<Typography variant={"caption"}>
|
|
131
|
+
This is a subcollection of <b>{parentCollection.name}</b>
|
|
132
|
+
</Typography>
|
|
133
|
+
</Chip>}
|
|
134
|
+
|
|
135
|
+
</div>
|
|
136
|
+
<div className={"grid grid-cols-12 gap-4"}>
|
|
137
|
+
|
|
138
|
+
<div className={"col-span-12"}>
|
|
139
|
+
<TextField
|
|
140
|
+
value={values.name ?? ""}
|
|
141
|
+
onChange={(e: any) => updateName(e.target.value)}
|
|
142
|
+
label={"Name"}
|
|
143
|
+
required
|
|
144
|
+
error={touched.name && Boolean(errors.name)}/>
|
|
145
|
+
<FieldHelperView error={touched.name && Boolean(errors.name)}>
|
|
146
|
+
{touched.name && Boolean(errors.name) ? errors.name : "Name of in this collection, usually a plural name (e.g. Products)"}
|
|
147
|
+
</FieldHelperView>
|
|
148
|
+
</div>
|
|
149
|
+
|
|
150
|
+
<div className={cn("col-span-12 ", isSubcollection ? "" : "sm:col-span-8")}>
|
|
151
|
+
<Field name={"path"}
|
|
152
|
+
as={DebouncedTextField}
|
|
153
|
+
label={"Path"}
|
|
154
|
+
disabled={!isNewCollection}
|
|
155
|
+
required
|
|
156
|
+
error={touched.path && Boolean(errors.path)}/>
|
|
157
|
+
|
|
158
|
+
<FieldHelperView error={touched.path && Boolean(errors.path)}>
|
|
159
|
+
{touched.path && Boolean(errors.path)
|
|
160
|
+
? errors.path
|
|
161
|
+
: isSubcollection ? "Relative path to the parent (no need to include the parent path)" : "Path that this collection is stored in, in the database"}
|
|
162
|
+
</FieldHelperView>
|
|
163
|
+
|
|
164
|
+
</div>
|
|
165
|
+
|
|
166
|
+
{!isSubcollection && <div className={"col-span-12 sm:col-span-4 relative"}>
|
|
167
|
+
|
|
168
|
+
<TextField error={touched.group && Boolean(errors.group)}
|
|
169
|
+
disabled={isSubmitting}
|
|
170
|
+
value={values.group ?? ""}
|
|
171
|
+
autoComplete="off"
|
|
172
|
+
onChange={(event) => setFieldValue("group", event.target.value)}
|
|
173
|
+
name={"group"}
|
|
174
|
+
inputRef={groupRef}
|
|
175
|
+
label="Group"/>
|
|
176
|
+
<Autocomplete
|
|
177
|
+
open={autoCompleteOpen && (groupOptions ?? []).length > 0}
|
|
178
|
+
setOpen={setAutoCompleteOpen}>
|
|
179
|
+
{groupOptions?.map((group, index) => {
|
|
180
|
+
return <AutocompleteItem
|
|
181
|
+
key={index + "_" + group}
|
|
182
|
+
onClick={() => {
|
|
183
|
+
setAutoCompleteOpen(false);
|
|
184
|
+
setFieldValue("group", group ?? null);
|
|
185
|
+
}}
|
|
186
|
+
>
|
|
187
|
+
<div className={"flex-grow"}>
|
|
188
|
+
{group}
|
|
189
|
+
</div>
|
|
190
|
+
</AutocompleteItem>;
|
|
191
|
+
})}
|
|
192
|
+
</Autocomplete>
|
|
193
|
+
<FieldHelperView>
|
|
194
|
+
{touched.group && Boolean(errors.group) ? errors.group : "Group of the collection"}
|
|
195
|
+
</FieldHelperView>
|
|
196
|
+
</div>}
|
|
197
|
+
|
|
198
|
+
<div className={"col-span-12"}>
|
|
199
|
+
<ExpandablePanel
|
|
200
|
+
expanded={advancedPanelExpanded}
|
|
201
|
+
onExpandedChange={setAdvancedPanelExpanded}
|
|
202
|
+
title={
|
|
203
|
+
<div className="flex flex-row text-gray-500">
|
|
204
|
+
<SettingsIcon/>
|
|
205
|
+
<Typography variant={"subtitle2"}
|
|
206
|
+
className="ml-2">
|
|
207
|
+
Advanced
|
|
208
|
+
</Typography>
|
|
209
|
+
</div>}
|
|
210
|
+
initiallyExpanded={false}>
|
|
211
|
+
<div className={"grid grid-cols-12 gap-4 p-4"}>
|
|
212
|
+
|
|
213
|
+
<div className={"col-span-12"}>
|
|
214
|
+
<Field name={"id"}
|
|
215
|
+
as={DebouncedTextField}
|
|
216
|
+
disabled={!isNewCollection}
|
|
217
|
+
label={"Collection id"}
|
|
218
|
+
error={touched.id && Boolean(errors.id)}/>
|
|
219
|
+
<FieldHelperView error={touched.id && Boolean(errors.id)}>
|
|
220
|
+
{touched.id && Boolean(errors.id) ? errors.id : "This id identifies this collection"}
|
|
221
|
+
</FieldHelperView>
|
|
222
|
+
</div>
|
|
223
|
+
|
|
224
|
+
<div className={"col-span-12"}>
|
|
225
|
+
<TextField
|
|
226
|
+
error={touched.singularName && Boolean(errors.singularName)}
|
|
227
|
+
id={"singularName"}
|
|
228
|
+
aria-describedby={"singularName-helper"}
|
|
229
|
+
onChange={(e) => {
|
|
230
|
+
setFieldTouched("singularName", true);
|
|
231
|
+
return handleChange(e);
|
|
232
|
+
}}
|
|
233
|
+
value={values.singularName ?? ""}
|
|
234
|
+
label={"Singular name"}/>
|
|
235
|
+
<FieldHelperView error={touched.singularName && Boolean(errors.singularName)}>
|
|
236
|
+
{touched.singularName && Boolean(errors.singularName) ? errors.singularName : "Optionally define a singular name for your entities"}
|
|
237
|
+
</FieldHelperView>
|
|
238
|
+
</div>
|
|
239
|
+
<div className={"col-span-12"}>
|
|
240
|
+
<TextField
|
|
241
|
+
error={touched.description && Boolean(errors.description)}
|
|
242
|
+
id="description"
|
|
243
|
+
value={values.description ?? ""}
|
|
244
|
+
onChange={handleChange}
|
|
245
|
+
multiline
|
|
246
|
+
rows={2}
|
|
247
|
+
aria-describedby="description-helper-text"
|
|
248
|
+
label="Description"
|
|
249
|
+
/>
|
|
250
|
+
<FieldHelperView error={touched.description && Boolean(errors.description)}>
|
|
251
|
+
{touched.description && Boolean(errors.description) ? errors.description : "Description of the collection, you can use markdown"}
|
|
252
|
+
</FieldHelperView>
|
|
253
|
+
</div>
|
|
254
|
+
|
|
255
|
+
<div className={"col-span-12"}>
|
|
256
|
+
<Select
|
|
257
|
+
name="defaultSize"
|
|
258
|
+
label="Default row size"
|
|
259
|
+
position={"item-aligned"}
|
|
260
|
+
onChange={handleChange}
|
|
261
|
+
value={values.defaultSize ?? ""}
|
|
262
|
+
renderValue={(value: any) => value.toUpperCase()}
|
|
263
|
+
>
|
|
264
|
+
{["xs", "s", "m", "l", "xl"].map((value) => (
|
|
265
|
+
<SelectItem
|
|
266
|
+
key={`size-select-${value}`}
|
|
267
|
+
value={value}>
|
|
268
|
+
{value.toUpperCase()}
|
|
269
|
+
</SelectItem>
|
|
270
|
+
))}
|
|
271
|
+
</Select>
|
|
272
|
+
</div>
|
|
273
|
+
<div className={"col-span-12"}>
|
|
274
|
+
<Select
|
|
275
|
+
name="customId"
|
|
276
|
+
label="Data IDs generation"
|
|
277
|
+
position={"item-aligned"}
|
|
278
|
+
disabled={customIdValue === "code_defined"}
|
|
279
|
+
onValueChange={(v) => {
|
|
280
|
+
if (v === "code_defined")
|
|
281
|
+
throw new Error("This should not happen");
|
|
282
|
+
else if (v === "true")
|
|
283
|
+
setFieldValue("customId", true);
|
|
284
|
+
else if (v === "false")
|
|
285
|
+
setFieldValue("customId", false);
|
|
286
|
+
else if (v === "optional")
|
|
287
|
+
setFieldValue("customId", "optional");
|
|
288
|
+
}}
|
|
289
|
+
value={customIdValue ?? ""}
|
|
290
|
+
renderValue={(value: any) => {
|
|
291
|
+
if (value === "code_defined")
|
|
292
|
+
return "Code defined";
|
|
293
|
+
else if (value === "true")
|
|
294
|
+
return "Users must define an ID";
|
|
295
|
+
else if (value === "optional")
|
|
296
|
+
return "Users can define an ID, but it is not required";
|
|
297
|
+
else
|
|
298
|
+
return "Document ID is generated automatically";
|
|
299
|
+
}}
|
|
300
|
+
>
|
|
301
|
+
<SelectItem value={"false"}>
|
|
302
|
+
Document ID is generated automatically
|
|
303
|
+
</SelectItem>
|
|
304
|
+
<SelectItem value={"true"}>
|
|
305
|
+
Users must define an ID
|
|
306
|
+
</SelectItem>
|
|
307
|
+
<SelectItem value={"optional"}>
|
|
308
|
+
Users can define an ID, but it is not required
|
|
309
|
+
</SelectItem>
|
|
310
|
+
</Select>
|
|
311
|
+
</div>
|
|
312
|
+
<div className={"col-span-12"}>
|
|
313
|
+
<BooleanSwitchWithLabel
|
|
314
|
+
position={"start"}
|
|
315
|
+
label="Collection group"
|
|
316
|
+
onValueChange={(v) => setFieldValue("collectionGroup", v)}
|
|
317
|
+
value={values.collectionGroup ?? false}
|
|
318
|
+
/>
|
|
319
|
+
<FieldHelperView>
|
|
320
|
+
A collection group consists of all collections with the same path. This allows
|
|
321
|
+
you
|
|
322
|
+
to query over multiple collections at once.
|
|
323
|
+
</FieldHelperView>
|
|
324
|
+
</div>
|
|
325
|
+
<div className={"col-span-12"}>
|
|
326
|
+
<BooleanSwitchWithLabel
|
|
327
|
+
position={"start"}
|
|
328
|
+
label="Enable text search for this collection"
|
|
329
|
+
onValueChange={(v) => setFieldValue("textSearchEnabled", v)}
|
|
330
|
+
value={values.textSearchEnabled ?? false}
|
|
331
|
+
/>
|
|
332
|
+
<FieldHelperView>
|
|
333
|
+
Allow text search for this collection. If you have not specified a text search
|
|
334
|
+
delegate, this will use the built-in local text search. This is not recommended
|
|
335
|
+
for large collections, as it may incur in performance and cost issues.
|
|
336
|
+
</FieldHelperView>
|
|
337
|
+
</div>
|
|
338
|
+
</div>
|
|
339
|
+
</ExpandablePanel>
|
|
340
|
+
|
|
341
|
+
</div>
|
|
342
|
+
|
|
343
|
+
</div>
|
|
344
|
+
|
|
345
|
+
<div style={{ height: "52px" }}/>
|
|
346
|
+
|
|
347
|
+
<Dialog
|
|
348
|
+
open={iconDialogOpen}
|
|
349
|
+
onOpenChange={setIconDialogOpen}
|
|
350
|
+
maxWidth={"xl"}
|
|
351
|
+
fullWidth
|
|
352
|
+
>
|
|
353
|
+
<div className={"p-4 overflow-auto min-h-[200px]"}>
|
|
354
|
+
<SearchIconsView selectedIcon={values.icon}
|
|
355
|
+
onIconSelected={(icon: string) => {
|
|
356
|
+
setIconDialogOpen(false);
|
|
357
|
+
setFieldValue("icon", icon);
|
|
358
|
+
}}/>
|
|
359
|
+
</div>
|
|
360
|
+
|
|
361
|
+
</Dialog>
|
|
362
|
+
|
|
363
|
+
</Container>
|
|
364
|
+
</div>
|
|
365
|
+
);
|
|
366
|
+
}
|