@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.
Files changed (145) hide show
  1. package/dist/ConfigControllerProvider.d.ts +3 -2
  2. package/dist/index.d.ts +3 -2
  3. package/dist/index.es.js +3128 -4094
  4. package/dist/index.es.js.map +1 -1
  5. package/dist/index.umd.js +3 -1
  6. package/dist/index.umd.js.map +1 -1
  7. package/dist/types/collection_editor_controller.d.ts +22 -7
  8. package/dist/types/collection_inference.d.ts +1 -1
  9. package/dist/types/config_controller.d.ts +32 -5
  10. package/dist/types/persisted_collection.d.ts +3 -1
  11. package/dist/ui/CollectionViewHeaderAction.d.ts +10 -0
  12. package/dist/{components → ui}/EditorCollectionAction.d.ts +1 -1
  13. package/dist/ui/MissingReferenceWidget.d.ts +3 -0
  14. package/dist/ui/NewCollectionButton.d.ts +1 -0
  15. package/dist/ui/PropertyAddColumnComponent.d.ts +6 -0
  16. package/dist/ui/RootCollectionSuggestions.d.ts +1 -0
  17. package/dist/{components → ui}/collection_editor/CollectionDetailsForm.d.ts +3 -2
  18. package/dist/{components → ui}/collection_editor/CollectionEditorDialog.d.ts +11 -6
  19. package/dist/{components → ui}/collection_editor/CollectionPropertiesEditorForm.d.ts +6 -3
  20. package/dist/{components → ui}/collection_editor/CollectionYupValidation.d.ts +3 -0
  21. package/dist/ui/collection_editor/EntityCustomViewsSelectDialog.d.ts +4 -0
  22. package/dist/ui/collection_editor/GetCodeDialog.d.ts +5 -0
  23. package/dist/{components → ui}/collection_editor/PropertyEditView.d.ts +8 -6
  24. package/dist/{components → ui}/collection_editor/PropertyFieldPreview.d.ts +4 -3
  25. package/dist/ui/collection_editor/PropertySelectItem.d.ts +8 -0
  26. package/dist/{components → ui}/collection_editor/PropertyTree.d.ts +8 -5
  27. package/dist/{components → ui}/collection_editor/SubcollectionsEditTab.d.ts +2 -2
  28. package/dist/{components → ui}/collection_editor/import/CollectionEditorImportDataPreview.d.ts +1 -1
  29. package/dist/ui/collection_editor/import/CollectionEditorImportMapping.d.ts +7 -0
  30. package/dist/{components → ui}/collection_editor/import/clean_import_data.d.ts +1 -1
  31. package/dist/{components → ui}/collection_editor/properties/BlockPropertyField.d.ts +4 -1
  32. package/dist/{components → ui}/collection_editor/properties/CommonPropertyFields.d.ts +1 -0
  33. package/dist/{components → ui}/collection_editor/properties/MapPropertyField.d.ts +4 -1
  34. package/dist/{components → ui}/collection_editor/properties/RepeatPropertyField.d.ts +4 -1
  35. package/dist/{components → ui}/collection_editor/properties/StringPropertyField.d.ts +1 -1
  36. package/dist/ui/collection_editor/properties/UrlPropertyField.d.ts +4 -0
  37. package/dist/ui/collection_editor/templates/blog_template.d.ts +2 -0
  38. package/dist/ui/collection_editor/templates/pages_template.d.ts +2 -0
  39. package/dist/ui/collection_editor/templates/products_template.d.ts +2 -0
  40. package/dist/ui/collection_editor/templates/users_template.d.ts +2 -0
  41. package/dist/ui/collection_editor/utils/strings.d.ts +1 -0
  42. package/dist/ui/collection_editor/utils/supported_fields.d.ts +3 -0
  43. package/dist/ui/collection_editor/utils/update_property_for_widget.d.ts +2 -0
  44. package/dist/useCollectionEditorPlugin.d.ts +5 -3
  45. package/dist/utils/entities.d.ts +3 -4
  46. package/package.json +22 -19
  47. package/src/ConfigControllerProvider.tsx +336 -0
  48. package/src/index.ts +35 -0
  49. package/src/types/collection_editor_controller.tsx +42 -0
  50. package/src/types/collection_inference.ts +3 -0
  51. package/src/types/config_controller.tsx +60 -0
  52. package/src/types/config_permissions.ts +20 -0
  53. package/src/types/persisted_collection.ts +9 -0
  54. package/src/ui/CollectionViewHeaderAction.tsx +43 -0
  55. package/src/ui/EditorCollectionAction.tsx +109 -0
  56. package/src/ui/HomePageEditorCollectionAction.tsx +84 -0
  57. package/src/ui/MissingReferenceWidget.tsx +35 -0
  58. package/src/ui/NewCollectionButton.tsx +16 -0
  59. package/src/ui/NewCollectionCard.tsx +47 -0
  60. package/src/ui/PropertyAddColumnComponent.tsx +42 -0
  61. package/src/ui/RootCollectionSuggestions.tsx +55 -0
  62. package/src/ui/collection_editor/CollectionDetailsForm.tsx +366 -0
  63. package/src/ui/collection_editor/CollectionEditorDialog.tsx +754 -0
  64. package/src/ui/collection_editor/CollectionEditorWelcomeView.tsx +206 -0
  65. package/src/ui/collection_editor/CollectionPropertiesEditorForm.tsx +481 -0
  66. package/src/ui/collection_editor/CollectionYupValidation.tsx +7 -0
  67. package/src/ui/collection_editor/EntityCustomViewsSelectDialog.tsx +37 -0
  68. package/src/ui/collection_editor/EnumForm.tsx +354 -0
  69. package/src/ui/collection_editor/GetCodeDialog.tsx +110 -0
  70. package/src/ui/collection_editor/PropertyEditView.tsx +558 -0
  71. package/src/ui/collection_editor/PropertyFieldPreview.tsx +203 -0
  72. package/src/ui/collection_editor/PropertySelectItem.tsx +32 -0
  73. package/src/ui/collection_editor/PropertyTree.tsx +233 -0
  74. package/src/ui/collection_editor/SubcollectionsEditTab.tsx +253 -0
  75. package/src/ui/collection_editor/UnsavedChangesDialog.tsx +47 -0
  76. package/src/ui/collection_editor/import/CollectionEditorImportDataPreview.tsx +37 -0
  77. package/src/ui/collection_editor/import/CollectionEditorImportMapping.tsx +260 -0
  78. package/src/ui/collection_editor/import/clean_import_data.ts +53 -0
  79. package/src/ui/collection_editor/properties/BlockPropertyField.tsx +135 -0
  80. package/src/ui/collection_editor/properties/BooleanPropertyField.tsx +36 -0
  81. package/src/ui/collection_editor/properties/CommonPropertyFields.tsx +137 -0
  82. package/src/ui/collection_editor/properties/DateTimePropertyField.tsx +87 -0
  83. package/src/ui/collection_editor/properties/EnumPropertyField.tsx +117 -0
  84. package/src/ui/collection_editor/properties/FieldHelperView.tsx +13 -0
  85. package/src/ui/collection_editor/properties/KeyValuePropertyField.tsx +20 -0
  86. package/src/ui/collection_editor/properties/MapPropertyField.tsx +149 -0
  87. package/src/ui/collection_editor/properties/NumberPropertyField.tsx +38 -0
  88. package/src/ui/collection_editor/properties/ReferencePropertyField.tsx +165 -0
  89. package/src/ui/collection_editor/properties/RepeatPropertyField.tsx +108 -0
  90. package/src/ui/collection_editor/properties/StoragePropertyField.tsx +194 -0
  91. package/src/ui/collection_editor/properties/StringPropertyField.tsx +79 -0
  92. package/src/ui/collection_editor/properties/UrlPropertyField.tsx +89 -0
  93. package/src/ui/collection_editor/properties/advanced/AdvancedPropertyValidation.tsx +36 -0
  94. package/src/ui/collection_editor/properties/validation/ArrayPropertyValidation.tsx +50 -0
  95. package/src/ui/collection_editor/properties/validation/GeneralPropertyValidation.tsx +50 -0
  96. package/src/ui/collection_editor/properties/validation/NumberPropertyValidation.tsx +100 -0
  97. package/src/ui/collection_editor/properties/validation/StringPropertyValidation.tsx +132 -0
  98. package/src/ui/collection_editor/properties/validation/ValidationPanel.tsx +28 -0
  99. package/src/ui/collection_editor/templates/blog_template.ts +115 -0
  100. package/src/ui/collection_editor/templates/pages_template.ts +188 -0
  101. package/src/ui/collection_editor/templates/products_template.ts +88 -0
  102. package/src/ui/collection_editor/templates/users_template.ts +42 -0
  103. package/src/ui/collection_editor/util.ts +21 -0
  104. package/src/ui/collection_editor/utils/strings.ts +8 -0
  105. package/src/ui/collection_editor/utils/supported_fields.tsx +29 -0
  106. package/src/ui/collection_editor/utils/update_property_for_widget.ts +271 -0
  107. package/src/ui/collection_editor/utils/useTraceUpdate.tsx +23 -0
  108. package/src/useCollectionEditorController.tsx +9 -0
  109. package/src/useCollectionEditorPlugin.tsx +137 -0
  110. package/src/useCollectionsConfigController.tsx +9 -0
  111. package/src/utils/arrays.ts +3 -0
  112. package/src/utils/entities.ts +38 -0
  113. package/src/vite-env.d.ts +1 -0
  114. package/dist/components/collection_editor/PropertySelectItem.d.ts +0 -8
  115. package/dist/components/collection_editor/SelectIcons.d.ts +0 -6
  116. package/dist/components/collection_editor/import/CollectionEditorImportMapping.d.ts +0 -4
  117. package/dist/components/collection_editor/templates/blog_template.d.ts +0 -10
  118. package/dist/components/collection_editor/templates/products_template.d.ts +0 -12
  119. package/dist/components/collection_editor/templates/users_template.d.ts +0 -7
  120. package/dist/components/collection_editor/utils/supported_fields.d.ts +0 -3
  121. package/dist/components/collection_editor/utils/update_property_for_widget.d.ts +0 -3
  122. package/dist/types/editable_properties.d.ts +0 -10
  123. package/dist/utils/icons.d.ts +0 -2
  124. package/dist/utils/synonyms.d.ts +0 -1951
  125. /package/dist/{components → ui}/HomePageEditorCollectionAction.d.ts +0 -0
  126. /package/dist/{components → ui}/NewCollectionCard.d.ts +0 -0
  127. /package/dist/{components → ui}/collection_editor/CollectionEditorWelcomeView.d.ts +0 -0
  128. /package/dist/{components → ui}/collection_editor/EnumForm.d.ts +0 -0
  129. /package/dist/{components → ui}/collection_editor/UnsavedChangesDialog.d.ts +0 -0
  130. /package/dist/{components → ui}/collection_editor/properties/BooleanPropertyField.d.ts +0 -0
  131. /package/dist/{components → ui}/collection_editor/properties/DateTimePropertyField.d.ts +0 -0
  132. /package/dist/{components → ui}/collection_editor/properties/EnumPropertyField.d.ts +0 -0
  133. /package/dist/{components → ui}/collection_editor/properties/FieldHelperView.d.ts +0 -0
  134. /package/dist/{components → ui}/collection_editor/properties/KeyValuePropertyField.d.ts +0 -0
  135. /package/dist/{components → ui}/collection_editor/properties/NumberPropertyField.d.ts +0 -0
  136. /package/dist/{components → ui}/collection_editor/properties/ReferencePropertyField.d.ts +0 -0
  137. /package/dist/{components → ui}/collection_editor/properties/StoragePropertyField.d.ts +0 -0
  138. /package/dist/{components → ui}/collection_editor/properties/advanced/AdvancedPropertyValidation.d.ts +0 -0
  139. /package/dist/{components → ui}/collection_editor/properties/validation/ArrayPropertyValidation.d.ts +0 -0
  140. /package/dist/{components → ui}/collection_editor/properties/validation/GeneralPropertyValidation.d.ts +0 -0
  141. /package/dist/{components → ui}/collection_editor/properties/validation/NumberPropertyValidation.d.ts +0 -0
  142. /package/dist/{components → ui}/collection_editor/properties/validation/StringPropertyValidation.d.ts +0 -0
  143. /package/dist/{components → ui}/collection_editor/properties/validation/ValidationPanel.d.ts +0 -0
  144. /package/dist/{components → ui}/collection_editor/util.d.ts +0 -0
  145. /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&apos;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
+ }