@firecms/core 3.0.0-canary.4 → 3.0.0-canary.41
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/README.md +2 -2
- package/dist/components/ClearFilterSortButton.d.ts +5 -0
- package/dist/components/EntityCollectionTable/EntityCollectionRowActions.d.ts +11 -11
- package/dist/components/EntityCollectionTable/EntityCollectionTable.d.ts +2 -2
- package/dist/components/EntityCollectionTable/PropertyTableCell.d.ts +2 -2
- package/dist/components/EntityCollectionTable/internal/CollectionTableToolbar.d.ts +1 -4
- package/dist/components/EntityCollectionView/EntityCollectionView.d.ts +12 -3
- package/dist/components/EntityCollectionView/EntityCollectionViewStartActions.d.ts +11 -0
- package/dist/components/EntityCollectionView/useSelectionController.d.ts +2 -0
- package/dist/components/EntityPreview.d.ts +25 -7
- package/dist/components/EntityView.d.ts +11 -0
- package/dist/components/FieldCaption.d.ts +5 -0
- package/dist/components/HomePage/NavigationCard.d.ts +8 -0
- package/dist/components/HomePage/{NavigationCollectionCard.d.ts → NavigationCardBinding.d.ts} +2 -2
- package/dist/components/HomePage/SmallNavigationCard.d.ts +6 -0
- package/dist/components/HomePage/index.d.ts +3 -1
- package/dist/components/VirtualTable/VirtualTableProps.d.ts +1 -1
- package/dist/components/index.d.ts +4 -3
- package/dist/contexts/AuthControllerContext.d.ts +1 -1
- package/dist/{internal/EntityView.d.ts → core/EntityEditView.d.ts} +2 -2
- package/dist/core/SideEntityView.d.ts +7 -0
- package/dist/core/index.d.ts +0 -2
- package/dist/form/EntityForm.d.ts +1 -1
- package/dist/form/components/ErrorFocus.d.ts +1 -1
- package/dist/form/components/StorageItemPreview.d.ts +3 -2
- package/dist/form/components/StorageUploadProgress.d.ts +1 -1
- package/dist/form/components/index.d.ts +1 -0
- package/dist/form/field_bindings/KeyValueFieldBinding.d.ts +1 -1
- package/dist/form/field_bindings/MapFieldBinding.d.ts +1 -1
- package/dist/form/field_bindings/StorageUploadFieldBinding.d.ts +4 -3
- package/dist/form/field_bindings/TextFieldBinding.d.ts +2 -2
- package/dist/form/index.d.ts +1 -0
- package/dist/form/validation.d.ts +1 -1
- package/dist/hooks/data/delete.d.ts +2 -2
- package/dist/hooks/data/save.d.ts +1 -1
- package/dist/hooks/data/useDataSource.d.ts +2 -2
- package/dist/hooks/data/useEntityFetch.d.ts +3 -3
- package/dist/hooks/index.d.ts +3 -1
- package/dist/{core → hooks}/useBuildModeController.d.ts +1 -1
- package/dist/hooks/useBuildNavigationController.d.ts +6 -4
- package/dist/hooks/useProjectLog.d.ts +6 -2
- package/dist/hooks/useStorageSource.d.ts +2 -2
- package/dist/hooks/useValidateAuthenticator.d.ts +25 -0
- package/dist/index.es.js +8258 -7767
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +5 -5
- package/dist/index.umd.js.map +1 -1
- package/dist/internal/useBuildDataSource.d.ts +4 -0
- package/dist/preview/PropertyPreview.d.ts +1 -1
- package/dist/preview/PropertyPreviewProps.d.ts +1 -4
- package/dist/preview/components/BooleanPreview.d.ts +5 -1
- package/dist/preview/components/EnumValuesChip.d.ts +1 -1
- package/dist/preview/components/ReferencePreview.d.ts +1 -7
- package/dist/types/analytics.d.ts +1 -1
- package/dist/types/auth.d.ts +37 -1
- package/dist/types/collections.d.ts +22 -5
- package/dist/types/datasource.d.ts +1 -1
- package/dist/types/entities.d.ts +1 -1
- package/dist/types/entity_callbacks.d.ts +2 -2
- package/dist/types/entity_overrides.d.ts +6 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/navigation.d.ts +14 -13
- package/dist/types/permissions.d.ts +5 -1
- package/dist/types/plugins.d.ts +20 -20
- package/dist/types/properties.d.ts +2 -2
- package/dist/types/property_config.d.ts +2 -2
- package/dist/types/roles.d.ts +31 -0
- package/dist/types/storage.d.ts +11 -3
- package/dist/types/user.d.ts +5 -0
- package/dist/util/collections.d.ts +9 -1
- package/dist/util/entities.d.ts +1 -1
- package/dist/util/icons.d.ts +8 -2
- package/dist/util/navigation_utils.d.ts +2 -2
- package/dist/util/permissions.d.ts +4 -4
- package/dist/util/references.d.ts +4 -2
- package/dist/util/resolutions.d.ts +6 -6
- package/dist/util/useTraceUpdate.d.ts +1 -0
- package/package.json +27 -24
- package/src/components/ClearFilterSortButton.tsx +41 -0
- package/src/components/DeleteEntityDialog.tsx +4 -4
- package/src/components/EntityCollectionTable/EntityCollectionRowActions.tsx +2 -2
- package/src/components/EntityCollectionTable/EntityCollectionTable.tsx +268 -277
- package/src/components/EntityCollectionTable/EntityCollectionTableProps.tsx +1 -1
- package/src/components/EntityCollectionTable/PropertyTableCell.tsx +13 -13
- package/src/components/EntityCollectionTable/fields/TableReferenceField.tsx +9 -16
- package/src/components/EntityCollectionTable/fields/TableStorageUpload.tsx +3 -3
- package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +27 -32
- package/src/components/EntityCollectionTable/internal/default_entity_actions.tsx +9 -5
- package/src/components/EntityCollectionView/EntityCollectionView.tsx +51 -52
- package/src/components/EntityCollectionView/EntityCollectionViewActions.tsx +5 -6
- package/src/components/EntityCollectionView/EntityCollectionViewStartActions.tsx +68 -0
- package/src/components/EntityCollectionView/useSelectionController.tsx +30 -0
- package/src/components/EntityPreview.tsx +207 -70
- package/src/components/EntityView.tsx +84 -0
- package/src/components/FieldCaption.tsx +14 -0
- package/src/components/FireCMSAppBar.tsx +8 -0
- package/src/components/HomePage/DefaultHomePage.tsx +14 -10
- package/src/components/HomePage/NavigationCard.tsx +69 -0
- package/src/components/HomePage/NavigationCardBinding.tsx +116 -0
- package/src/components/HomePage/SmallNavigationCard.tsx +45 -0
- package/src/components/HomePage/index.tsx +3 -1
- package/src/components/ReferenceTable/ReferenceSelectionTable.tsx +3 -4
- package/src/components/ReferenceWidget.tsx +4 -4
- package/src/components/SelectableTable/filters/DateTimeFilterField.tsx +23 -8
- package/src/components/SelectableTable/filters/ReferenceFilterField.tsx +35 -24
- package/src/components/SelectableTable/filters/StringNumberFilterField.tsx +35 -15
- package/src/components/VirtualTable/VirtualTable.tsx +17 -7
- package/src/components/VirtualTable/VirtualTableProps.tsx +1 -1
- package/src/components/VirtualTable/fields/VirtualTableDateField.tsx +1 -1
- package/src/components/common/useDataSourceEntityCollectionTableController.tsx +1 -1
- package/src/components/index.tsx +4 -3
- package/src/contexts/AuthControllerContext.tsx +1 -1
- package/src/core/Drawer.tsx +66 -39
- package/src/{internal/EntityView.tsx → core/EntityEditView.tsx} +22 -39
- package/src/core/EntitySidePanel.tsx +2 -2
- package/src/core/FireCMS.tsx +18 -3
- package/src/core/NavigationRoutes.tsx +8 -0
- package/src/core/SideEntityView.tsx +38 -0
- package/src/core/field_configs.tsx +1 -2
- package/src/core/index.tsx +0 -2
- package/src/form/EntityForm.tsx +20 -12
- package/src/form/components/StorageItemPreview.tsx +5 -3
- package/src/form/components/StorageUploadProgress.tsx +6 -5
- package/src/form/components/index.tsx +1 -0
- package/src/form/field_bindings/ArrayCustomShapedFieldBinding.tsx +2 -3
- package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +12 -15
- package/src/form/field_bindings/BlockFieldBinding.tsx +2 -3
- package/src/form/field_bindings/DateTimeFieldBinding.tsx +4 -4
- package/src/form/field_bindings/KeyValueFieldBinding.tsx +18 -18
- package/src/form/field_bindings/MapFieldBinding.tsx +17 -17
- package/src/form/field_bindings/MarkdownFieldBinding.tsx +1 -2
- package/src/form/field_bindings/MultiSelectBinding.tsx +2 -3
- package/src/form/field_bindings/ReadOnlyFieldBinding.tsx +3 -3
- package/src/form/field_bindings/ReferenceFieldBinding.tsx +5 -3
- package/src/form/field_bindings/RepeatFieldBinding.tsx +3 -3
- package/src/form/field_bindings/SelectFieldBinding.tsx +2 -3
- package/src/form/field_bindings/StorageUploadFieldBinding.tsx +15 -6
- package/src/form/field_bindings/SwitchFieldBinding.tsx +2 -3
- package/src/form/field_bindings/TextFieldBinding.tsx +10 -9
- package/src/form/index.tsx +1 -0
- package/src/form/validation.ts +3 -4
- package/src/hooks/data/delete.ts +3 -3
- package/src/hooks/data/save.ts +1 -1
- package/src/hooks/data/useCollectionFetch.tsx +1 -1
- package/src/hooks/data/useDataSource.tsx +8 -3
- package/src/hooks/data/useEntityFetch.tsx +4 -4
- package/src/hooks/index.tsx +5 -1
- package/src/{core → hooks}/useBuildLocalConfigurationPersistence.tsx +9 -10
- package/src/{core → hooks}/useBuildModeController.tsx +12 -6
- package/src/hooks/useBuildNavigationController.tsx +197 -79
- package/src/hooks/useProjectLog.tsx +17 -7
- package/src/hooks/useReferenceDialog.tsx +2 -2
- package/src/hooks/useStorageSource.tsx +7 -2
- package/src/hooks/useValidateAuthenticator.tsx +135 -0
- package/src/internal/useBuildDataSource.ts +6 -1
- package/src/internal/useBuildSideEntityController.tsx +18 -12
- package/src/preview/PropertyPreview.tsx +1 -1
- package/src/preview/PropertyPreviewProps.tsx +1 -11
- package/src/preview/components/BooleanPreview.tsx +19 -4
- package/src/preview/components/EnumValuesChip.tsx +1 -1
- package/src/preview/components/ReferencePreview.tsx +55 -147
- package/src/preview/property_previews/StringPropertyPreview.tsx +8 -7
- package/src/types/analytics.ts +1 -0
- package/src/types/auth.tsx +50 -1
- package/src/types/collections.ts +24 -5
- package/src/types/datasource.ts +1 -1
- package/src/types/entities.ts +1 -1
- package/src/types/entity_actions.tsx +4 -0
- package/src/types/entity_callbacks.ts +2 -2
- package/src/types/entity_overrides.tsx +7 -0
- package/src/types/firecms.tsx +0 -1
- package/src/types/index.ts +2 -0
- package/src/types/navigation.ts +17 -16
- package/src/types/permissions.ts +6 -1
- package/src/types/plugins.tsx +26 -28
- package/src/types/properties.ts +3 -2
- package/src/types/property_config.tsx +2 -2
- package/src/types/roles.ts +41 -0
- package/src/types/side_entity_controller.tsx +1 -0
- package/src/types/storage.ts +12 -3
- package/src/types/user.ts +7 -0
- package/src/util/collections.ts +22 -0
- package/src/util/entities.ts +1 -1
- package/src/util/icons.tsx +11 -3
- package/src/util/navigation_utils.ts +6 -6
- package/src/util/permissions.ts +11 -8
- package/src/util/references.ts +36 -5
- package/src/util/strings.ts +2 -2
- package/src/util/useTraceUpdate.tsx +2 -1
- package/dist/internal/useLocaleConfig.d.ts +0 -1
- package/src/components/HomePage/NavigationCollectionCard.tsx +0 -146
- package/src/internal/useLocaleConfig.tsx +0 -18
- /package/dist/{components → form/components}/LabelWithIcon.d.ts +0 -0
- /package/dist/{core → hooks}/useBuildLocalConfigurationPersistence.d.ts +0 -0
- /package/src/{components → form/components}/LabelWithIcon.tsx +0 -0
package/src/core/Drawer.tsx
CHANGED
|
@@ -2,10 +2,10 @@ import React, { useCallback } from "react";
|
|
|
2
2
|
|
|
3
3
|
import { useLargeLayout, useNavigationController } from "../hooks";
|
|
4
4
|
|
|
5
|
-
import { NavLink } from "react-router-dom";
|
|
5
|
+
import { NavLink, useNavigate } from "react-router-dom";
|
|
6
6
|
import { CMSAnalyticsEvent, TopNavigationEntry, TopNavigationResult } from "../types";
|
|
7
7
|
import { IconForView } from "../util";
|
|
8
|
-
import { cn, Tooltip, Typography } from "@firecms/ui";
|
|
8
|
+
import { cn, IconButton, Menu, MenuItem, MoreVertIcon, Tooltip, Typography } from "@firecms/ui";
|
|
9
9
|
import { useAnalyticsController } from "../hooks/useAnalyticsController";
|
|
10
10
|
|
|
11
11
|
/**
|
|
@@ -33,6 +33,9 @@ export function Drawer({
|
|
|
33
33
|
|
|
34
34
|
const tooltipsOpen = hovered && !drawerOpen;
|
|
35
35
|
const largeLayout = useLargeLayout();
|
|
36
|
+
const navigate = useNavigate();
|
|
37
|
+
|
|
38
|
+
const [adminMenuOpen, setAdminMenuOpen] = React.useState(false);
|
|
36
39
|
|
|
37
40
|
if (!navigation.topLevelNavigation)
|
|
38
41
|
throw Error("Navigation not ready in Drawer");
|
|
@@ -42,7 +45,8 @@ export function Drawer({
|
|
|
42
45
|
groups
|
|
43
46
|
}: TopNavigationResult = navigation.topLevelNavigation;
|
|
44
47
|
|
|
45
|
-
const
|
|
48
|
+
const adminViews = navigationEntries.filter(e => e.type === "admin") ?? [];
|
|
49
|
+
const groupsWithoutAdmin = groups.filter(g => g !== "Admin");
|
|
46
50
|
|
|
47
51
|
const buildGroupHeader = useCallback((group?: string) => {
|
|
48
52
|
if (!drawerOpen) return <div className="h-12 w-full"/>;
|
|
@@ -67,41 +71,64 @@ export function Drawer({
|
|
|
67
71
|
};
|
|
68
72
|
|
|
69
73
|
return (
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
{
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
.
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
74
|
+
<>
|
|
75
|
+
|
|
76
|
+
<div className={"flex-grow overflow-scroll no-scrollbar"}>
|
|
77
|
+
|
|
78
|
+
{groupsWithoutAdmin.map((group) => (
|
|
79
|
+
<React.Fragment
|
|
80
|
+
key={`drawer_group_${group}`}>
|
|
81
|
+
{buildGroupHeader(group)}
|
|
82
|
+
{Object.values(navigationEntries)
|
|
83
|
+
.filter(e => e.group === group)
|
|
84
|
+
.map((view, index) =>
|
|
85
|
+
<DrawerNavigationItem
|
|
86
|
+
key={`navigation_${index}`}
|
|
87
|
+
icon={<IconForView collectionOrView={view.collection ?? view.view}/>}
|
|
88
|
+
tooltipsOpen={tooltipsOpen}
|
|
89
|
+
drawerOpen={drawerOpen}
|
|
90
|
+
onClick={() => onClick(view)}
|
|
91
|
+
url={view.url}
|
|
92
|
+
name={view.name}/>)}
|
|
93
|
+
</React.Fragment>
|
|
94
|
+
))}
|
|
95
|
+
|
|
96
|
+
</div>
|
|
97
|
+
|
|
98
|
+
{adminViews.length > 0 && <Menu
|
|
99
|
+
open={adminMenuOpen}
|
|
100
|
+
onOpenChange={setAdminMenuOpen}
|
|
101
|
+
trigger={
|
|
102
|
+
<IconButton
|
|
103
|
+
shape={"square"}
|
|
104
|
+
className={"m-4 text-gray-900 dark:text-white w-fit"}>
|
|
105
|
+
<Tooltip title={"Admin"}
|
|
106
|
+
open={tooltipsOpen}
|
|
107
|
+
side={"right"} sideOffset={28}>
|
|
108
|
+
<MoreVertIcon/>
|
|
109
|
+
</Tooltip>
|
|
110
|
+
{drawerOpen && <div
|
|
111
|
+
className={cn(
|
|
112
|
+
drawerOpen ? "opacity-100" : "opacity-0 hidden",
|
|
113
|
+
"mx-4 font-inherit text-inherit"
|
|
114
|
+
)}>
|
|
115
|
+
ADMIN
|
|
116
|
+
</div>}
|
|
117
|
+
</IconButton>}
|
|
118
|
+
>
|
|
119
|
+
{adminViews.map((entry, index) =>
|
|
120
|
+
<MenuItem
|
|
121
|
+
onClick={(event) => {
|
|
122
|
+
event.preventDefault();
|
|
123
|
+
navigate(entry.path);
|
|
124
|
+
}}
|
|
125
|
+
key={`navigation_${index}`}>
|
|
126
|
+
{<IconForView collectionOrView={entry.view}/>}
|
|
127
|
+
{entry.name}
|
|
128
|
+
</MenuItem>)}
|
|
129
|
+
|
|
130
|
+
</Menu>}
|
|
131
|
+
</>
|
|
105
132
|
);
|
|
106
133
|
}
|
|
107
134
|
|
|
@@ -133,7 +160,7 @@ export function DrawerNavigationItem({
|
|
|
133
160
|
transition: drawerOpen ? "width 150ms ease-in" : undefined
|
|
134
161
|
}}
|
|
135
162
|
className={({ isActive }: any) => cn("rounded-r-xl truncate",
|
|
136
|
-
"hover:bg-slate-300 hover:bg-opacity-75 dark:hover:bg-gray-700 dark:hover:bg-opacity-75 text-gray-800 dark:text-gray-200 hover:text-gray-900 hover:dark:text-
|
|
163
|
+
"hover:bg-slate-300 hover:bg-opacity-75 dark:hover:bg-gray-700 dark:hover:bg-opacity-75 text-gray-800 dark:text-gray-200 hover:text-gray-900 hover:dark:text-white",
|
|
137
164
|
"flex flex-row items-center mr-8",
|
|
138
165
|
// "transition-all ease-in-out delay-100 duration-300",
|
|
139
166
|
// drawerOpen ? "w-full" : "w-18",
|
|
@@ -9,10 +9,9 @@ import {
|
|
|
9
9
|
FormContext,
|
|
10
10
|
User
|
|
11
11
|
} from "../types";
|
|
12
|
-
import { CircularProgressCenter, EntityCollectionView,
|
|
12
|
+
import { CircularProgressCenter, EntityCollectionView, EntityView, ErrorBoundary, } from "../components";
|
|
13
13
|
import {
|
|
14
14
|
canEditEntity,
|
|
15
|
-
fullPathToCollectionSegments,
|
|
16
15
|
removeInitialAndTrailingSlashes,
|
|
17
16
|
resolveDefaultSelectedView,
|
|
18
17
|
resolveEntityView,
|
|
@@ -32,12 +31,12 @@ import {
|
|
|
32
31
|
import { EntityForm } from "../form";
|
|
33
32
|
import { CircularProgress, CloseIcon, cn, defaultBorderMixin, IconButton, Tab, Tabs, Typography } from "@firecms/ui";
|
|
34
33
|
import { EntityFormSaveParams } from "../form/EntityForm";
|
|
35
|
-
import { FORM_CONTAINER_WIDTH } from "
|
|
36
|
-
import { useSideDialogContext } from "
|
|
34
|
+
import { FORM_CONTAINER_WIDTH } from "../internal/common";
|
|
35
|
+
import { useSideDialogContext } from "./index";
|
|
37
36
|
|
|
38
37
|
const MAIN_TAB_VALUE = "main_##Q$SC^#S6";
|
|
39
38
|
|
|
40
|
-
export interface
|
|
39
|
+
export interface EntityEditViewProps<M extends Record<string, any>> {
|
|
41
40
|
path: string;
|
|
42
41
|
collection: EntityCollection<M>;
|
|
43
42
|
entityId?: string;
|
|
@@ -56,18 +55,18 @@ export interface EntityViewProps<M extends Record<string, any>> {
|
|
|
56
55
|
* You probably don't want to use this view directly since it is bound to the
|
|
57
56
|
* side panel. Instead, you might want to use {@link EntityForm} or {@link EntityCollectionView}
|
|
58
57
|
*/
|
|
59
|
-
export function
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
58
|
+
export function EntityEditView<M extends Record<string, any>, UserType extends User>({
|
|
59
|
+
path,
|
|
60
|
+
entityId,
|
|
61
|
+
selectedSubPath,
|
|
62
|
+
copy,
|
|
63
|
+
collection,
|
|
64
|
+
parentCollectionIds,
|
|
65
|
+
onValuesAreModified,
|
|
66
|
+
formWidth,
|
|
67
|
+
onUpdate,
|
|
68
|
+
onClose,
|
|
69
|
+
}: EntityEditViewProps<M>) {
|
|
71
70
|
|
|
72
71
|
if (collection.customId && collection.formAutoSave) {
|
|
73
72
|
console.warn(`The collection ${collection.path} has customId and formAutoSave enabled. This is not supported and formAutoSave will be ignored`);
|
|
@@ -95,7 +94,7 @@ export function EntityView<M extends Record<string, any>, UserType extends User>
|
|
|
95
94
|
|
|
96
95
|
const resolvedFormWidth: string = typeof formWidth === "number" ? `${formWidth}px` : formWidth ?? FORM_CONTAINER_WIDTH;
|
|
97
96
|
|
|
98
|
-
const dataSource = useDataSource();
|
|
97
|
+
const dataSource = useDataSource(collection);
|
|
99
98
|
const sideDialogContext = useSideDialogContext();
|
|
100
99
|
const sideEntityController = useSideEntityController();
|
|
101
100
|
const snackbarController = useSnackbarController();
|
|
@@ -154,29 +153,12 @@ export function EntityView<M extends Record<string, any>, UserType extends User>
|
|
|
154
153
|
if (status === "new") {
|
|
155
154
|
setReadOnly(false);
|
|
156
155
|
} else {
|
|
157
|
-
const editEnabled = usedEntity ? canEditEntity(collection, authController,
|
|
156
|
+
const editEnabled = usedEntity ? canEditEntity(collection, authController, path, usedEntity ?? null) : false;
|
|
158
157
|
if (usedEntity)
|
|
159
158
|
setReadOnly(!editEnabled);
|
|
160
159
|
}
|
|
161
160
|
}, [authController, usedEntity, status]);
|
|
162
161
|
|
|
163
|
-
// useEffect(() => {
|
|
164
|
-
// if (largeLayoutTabSelected.current === largeLayout)
|
|
165
|
-
// return;
|
|
166
|
-
// // open first tab by default in large layouts
|
|
167
|
-
// if (selectedSubPath !== defaultSelectedView) {
|
|
168
|
-
// console.log("Replacing url 1", defaultSelectedView);
|
|
169
|
-
// sideEntityController.replace({
|
|
170
|
-
// path,
|
|
171
|
-
// entityId,
|
|
172
|
-
// selectedSubPath: defaultSelectedView,
|
|
173
|
-
// updateUrl: true,
|
|
174
|
-
// collection
|
|
175
|
-
// });
|
|
176
|
-
// }
|
|
177
|
-
// largeLayoutTabSelected.current = largeLayout;
|
|
178
|
-
// }, [defaultSelectedView, largeLayout, selectedSubPath]);
|
|
179
|
-
|
|
180
162
|
const onPreSaveHookError = useCallback((e: Error) => {
|
|
181
163
|
setSaving(false);
|
|
182
164
|
snackbarController.open({
|
|
@@ -222,7 +204,7 @@ export function EntityView<M extends Record<string, any>, UserType extends User>
|
|
|
222
204
|
entityId: updatedEntity.id,
|
|
223
205
|
selectedSubPath: selectedTabRef.current,
|
|
224
206
|
updateUrl: true,
|
|
225
|
-
collection
|
|
207
|
+
collection,
|
|
226
208
|
});
|
|
227
209
|
}
|
|
228
210
|
|
|
@@ -302,6 +284,7 @@ export function EntityView<M extends Record<string, any>, UserType extends User>
|
|
|
302
284
|
.map(e => resolveEntityView(e, customizationController.entityViews))
|
|
303
285
|
.filter(Boolean) as EntityCustomView[]
|
|
304
286
|
: [];
|
|
287
|
+
|
|
305
288
|
const customViewsView: React.ReactNode[] | undefined = customViews && resolvedEntityViews
|
|
306
289
|
.map(
|
|
307
290
|
(customView, colIndex) => {
|
|
@@ -382,7 +365,7 @@ export function EntityView<M extends Record<string, any>, UserType extends User>
|
|
|
382
365
|
entityId,
|
|
383
366
|
selectedSubPath: value === MAIN_TAB_VALUE ? undefined : value,
|
|
384
367
|
updateUrl: true,
|
|
385
|
-
collection
|
|
368
|
+
collection,
|
|
386
369
|
});
|
|
387
370
|
};
|
|
388
371
|
|
|
@@ -463,7 +446,7 @@ export function EntityView<M extends Record<string, any>, UserType extends User>
|
|
|
463
446
|
className={"mt-16 mb-8 mx-8"}
|
|
464
447
|
variant={"h4"}>{collection.singularName ?? collection.name}
|
|
465
448
|
</Typography>
|
|
466
|
-
<
|
|
449
|
+
<EntityView
|
|
467
450
|
className={"px-12"}
|
|
468
451
|
entity={usedEntity as Entity<M>}
|
|
469
452
|
path={path}
|
|
@@ -4,7 +4,7 @@ import { EntitySidePanelProps } from "../types";
|
|
|
4
4
|
import { useNavigationController } from "../hooks";
|
|
5
5
|
|
|
6
6
|
import { ErrorBoundary } from "../components";
|
|
7
|
-
import {
|
|
7
|
+
import { EntityEditView } from "./EntityEditView";
|
|
8
8
|
import { useSideDialogContext } from "./SideDialogs";
|
|
9
9
|
|
|
10
10
|
/**
|
|
@@ -77,7 +77,7 @@ export function EntitySidePanel(props: EntitySidePanelProps) {
|
|
|
77
77
|
return (
|
|
78
78
|
<>
|
|
79
79
|
<ErrorBoundary>
|
|
80
|
-
<
|
|
80
|
+
<EntityEditView
|
|
81
81
|
{...props}
|
|
82
82
|
formWidth={props.width}
|
|
83
83
|
collection={collection}
|
package/src/core/FireCMS.tsx
CHANGED
|
@@ -12,8 +12,7 @@ import { DataSourceContext } from "../contexts/DataSourceContext";
|
|
|
12
12
|
import { SideEntityControllerContext } from "../contexts/SideEntityControllerContext";
|
|
13
13
|
import { NavigationContext } from "../contexts/NavigationContext";
|
|
14
14
|
import { SideDialogsControllerContext } from "../contexts/SideDialogsControllerContext";
|
|
15
|
-
import { useLocaleConfig } from "
|
|
16
|
-
import { CenteredView } from "@firecms/ui";
|
|
15
|
+
import { CenteredView, Typography, useLocaleConfig } from "@firecms/ui";
|
|
17
16
|
import { DialogsProvider } from "../contexts/DialogsProvider";
|
|
18
17
|
import { useBuildDataSource } from "../internal/useBuildDataSource";
|
|
19
18
|
import { useBuildCustomizationController } from "../internal/useBuildCustomizationController";
|
|
@@ -85,7 +84,7 @@ export function FireCMS<UserType extends User, EC extends EntityCollection>(prop
|
|
|
85
84
|
onAnalyticsEvent
|
|
86
85
|
}), []);
|
|
87
86
|
|
|
88
|
-
useProjectLog(authController);
|
|
87
|
+
const accessResponse = useProjectLog(authController, plugins);
|
|
89
88
|
|
|
90
89
|
if (navigationController.navigationLoadingError) {
|
|
91
90
|
return (
|
|
@@ -107,6 +106,22 @@ export function FireCMS<UserType extends User, EC extends EntityCollection>(prop
|
|
|
107
106
|
);
|
|
108
107
|
}
|
|
109
108
|
|
|
109
|
+
if (accessResponse?.blocked) {
|
|
110
|
+
return (
|
|
111
|
+
<CenteredView maxWidth={"md"} fullScreen={true}>
|
|
112
|
+
<Typography variant={"h4"}>
|
|
113
|
+
Access blocked
|
|
114
|
+
</Typography>
|
|
115
|
+
<Typography>
|
|
116
|
+
This app has been blocked. Please reach out at <a
|
|
117
|
+
href={"mailto:hello@firecms.co"}>hello@firecms.co</a> for more information.
|
|
118
|
+
</Typography>
|
|
119
|
+
{accessResponse?.message &&
|
|
120
|
+
<Typography>Response from the server: {accessResponse?.message}</Typography>}
|
|
121
|
+
</CenteredView>
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
|
|
110
125
|
return (
|
|
111
126
|
<ModeControllerContext.Provider value={modeController}>
|
|
112
127
|
<AnalyticsContext.Provider value={analyticsController}>
|
|
@@ -59,6 +59,14 @@ export const NavigationRoutes = React.memo<NavigationRoutesProps>(
|
|
|
59
59
|
cmsViews.push(buildCMSViewRoute(cmsView.path, cmsView));
|
|
60
60
|
});
|
|
61
61
|
}
|
|
62
|
+
if (navigation.adminViews) {
|
|
63
|
+
navigation.adminViews.forEach((cmsView) => {
|
|
64
|
+
if (Array.isArray(cmsView.path))
|
|
65
|
+
cmsViews.push(...cmsView.path.map(path => buildCMSViewRoute(path, cmsView)));
|
|
66
|
+
else
|
|
67
|
+
cmsViews.push(buildCMSViewRoute(cmsView.path, cmsView));
|
|
68
|
+
});
|
|
69
|
+
}
|
|
62
70
|
|
|
63
71
|
// we reorder collections so that nested paths are included first
|
|
64
72
|
const sortedCollections = [...(navigation.collections ?? [])]
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { User } from "../types";
|
|
2
|
+
import { useSideDialogContext } from "./SideDialogs";
|
|
3
|
+
import { useSideEntityController } from "../hooks";
|
|
4
|
+
import { FORM_CONTAINER_WIDTH } from "../internal/common";
|
|
5
|
+
import { EntityEditViewProps } from "./EntityEditView";
|
|
6
|
+
|
|
7
|
+
export type SideEntityViewProps<M extends Record<string, any>> = EntityEditViewProps<M> & {
|
|
8
|
+
formWidth?: number | string;
|
|
9
|
+
onClose?: () => void;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function SideEntityView<M extends Record<string, any>, UserType extends User>({
|
|
13
|
+
path,
|
|
14
|
+
entityId,
|
|
15
|
+
selectedSubPath,
|
|
16
|
+
copy,
|
|
17
|
+
collection,
|
|
18
|
+
parentCollectionIds,
|
|
19
|
+
onValuesAreModified,
|
|
20
|
+
formWidth,
|
|
21
|
+
onUpdate,
|
|
22
|
+
onClose
|
|
23
|
+
}: SideEntityViewProps<M>) {
|
|
24
|
+
|
|
25
|
+
const sideDialogContext = useSideDialogContext();
|
|
26
|
+
const sideEntityController = useSideEntityController();
|
|
27
|
+
const resolvedFormWidth: string = typeof formWidth === "number" ? `${formWidth}px` : formWidth ?? FORM_CONTAINER_WIDTH;
|
|
28
|
+
|
|
29
|
+
const onCloseHandler = () => {
|
|
30
|
+
if (onClose) {
|
|
31
|
+
onClose();
|
|
32
|
+
} else {
|
|
33
|
+
sideDialogContext.close();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return <></>;
|
|
38
|
+
}
|
|
@@ -359,8 +359,7 @@ export function getDefaultFieldId(property: Property | ResolvedProperty) {
|
|
|
359
359
|
} else if (property.dataType === "map") {
|
|
360
360
|
if (property.keyValue)
|
|
361
361
|
return "key_value";
|
|
362
|
-
|
|
363
|
-
return "group";
|
|
362
|
+
return "group";
|
|
364
363
|
} else if (property.dataType === "array") {
|
|
365
364
|
const of = (property as ArrayProperty).of;
|
|
366
365
|
const oneOf = (property as ArrayProperty).oneOf;
|
package/src/core/index.tsx
CHANGED
package/src/form/EntityForm.tsx
CHANGED
|
@@ -19,8 +19,9 @@ import equal from "react-fast-compare"
|
|
|
19
19
|
import {
|
|
20
20
|
canCreateEntity,
|
|
21
21
|
canDeleteEntity,
|
|
22
|
-
fullPathToCollectionSegments,
|
|
23
22
|
getDefaultValuesFor,
|
|
23
|
+
getEntityTitlePropertyKey,
|
|
24
|
+
getValueInPath,
|
|
24
25
|
isHidden,
|
|
25
26
|
isReadOnly,
|
|
26
27
|
resolveCollection
|
|
@@ -171,7 +172,7 @@ function EntityFormInternal<M extends Record<string, any>>({
|
|
|
171
172
|
onFormContextChange,
|
|
172
173
|
hideId,
|
|
173
174
|
autoSave,
|
|
174
|
-
onIdUpdateError
|
|
175
|
+
onIdUpdateError,
|
|
175
176
|
}: EntityFormProps<M>) {
|
|
176
177
|
|
|
177
178
|
const analyticsController = useAnalyticsController();
|
|
@@ -179,7 +180,7 @@ function EntityFormInternal<M extends Record<string, any>>({
|
|
|
179
180
|
const customizationController = useCustomizationController();
|
|
180
181
|
|
|
181
182
|
const context = useFireCMSContext();
|
|
182
|
-
const dataSource = useDataSource();
|
|
183
|
+
const dataSource = useDataSource(inputCollection);
|
|
183
184
|
const plugins = customizationController.plugins;
|
|
184
185
|
|
|
185
186
|
const initialResolvedCollection = useMemo(() => resolveCollection({
|
|
@@ -344,6 +345,9 @@ function EntityFormInternal<M extends Record<string, any>>({
|
|
|
344
345
|
fields: customizationController.propertyConfigs
|
|
345
346
|
});
|
|
346
347
|
|
|
348
|
+
const titlePropertyKey = getEntityTitlePropertyKey(resolvedCollection, customizationController.propertyConfigs);
|
|
349
|
+
const title = internalValues && titlePropertyKey ? getValueInPath(internalValues, titlePropertyKey) : undefined;
|
|
350
|
+
|
|
347
351
|
const onIdUpdate = inputCollection.callbacks?.onIdUpdate;
|
|
348
352
|
|
|
349
353
|
const doOnIdUpdate = useCallback(async () => {
|
|
@@ -389,12 +393,15 @@ function EntityFormInternal<M extends Record<string, any>>({
|
|
|
389
393
|
|
|
390
394
|
const authController = useAuthController();
|
|
391
395
|
|
|
392
|
-
const getActionsForEntity = useCallback(({
|
|
396
|
+
const getActionsForEntity = useCallback(({
|
|
397
|
+
entity,
|
|
398
|
+
customEntityActions
|
|
399
|
+
}: {
|
|
393
400
|
entity?: Entity<M>,
|
|
394
401
|
customEntityActions?: EntityAction[]
|
|
395
402
|
}): EntityAction[] => {
|
|
396
|
-
const createEnabled = canCreateEntity(inputCollection, authController,
|
|
397
|
-
const deleteEnabled = entity ? canDeleteEntity(inputCollection, authController,
|
|
403
|
+
const createEnabled = canCreateEntity(inputCollection, authController, path, null);
|
|
404
|
+
const deleteEnabled = entity ? canDeleteEntity(inputCollection, authController, path, entity) : true;
|
|
398
405
|
const actions: EntityAction[] = [];
|
|
399
406
|
if (createEnabled)
|
|
400
407
|
actions.push(copyEntityAction);
|
|
@@ -436,7 +443,7 @@ function EntityFormInternal<M extends Record<string, any>>({
|
|
|
436
443
|
pluginActions.push(...plugins.map((plugin, i) => (
|
|
437
444
|
plugin.form?.Actions
|
|
438
445
|
? <plugin.form.Actions
|
|
439
|
-
key={`actions_${plugin.
|
|
446
|
+
key={`actions_${plugin.key}`} {...actionProps}/>
|
|
440
447
|
: null
|
|
441
448
|
)).filter(Boolean));
|
|
442
449
|
}
|
|
@@ -454,8 +461,8 @@ function EntityFormInternal<M extends Record<string, any>>({
|
|
|
454
461
|
className={`w-full py-2 flex flex-col items-start mt-${4 + (pluginActions ? 8 : 0)} lg:mt-${8 + (pluginActions ? 8 : 0)} mb-8`}>
|
|
455
462
|
|
|
456
463
|
<Typography
|
|
457
|
-
className={"mt-4 flex-grow " + inputCollection.hideIdFromForm ? "mb-2" : "mb-0"}
|
|
458
|
-
variant={"h4"}>{inputCollection.singularName ?? inputCollection.name}
|
|
464
|
+
className={"mt-4 flex-grow line-clamp-1 " + inputCollection.hideIdFromForm ? "mb-2" : "mb-0"}
|
|
465
|
+
variant={"h4"}>{title ?? inputCollection.singularName ?? inputCollection.name}
|
|
459
466
|
</Typography>
|
|
460
467
|
<Alert color={"base"} className={"w-full"} size={"small"}>
|
|
461
468
|
<code className={"text-xs select-all"}>{path}/{entityId}</code>
|
|
@@ -508,7 +515,7 @@ function InnerForm<M extends Record<string, any>>(props: FormexController<M> & {
|
|
|
508
515
|
savingError?: Error,
|
|
509
516
|
closeAfterSaveRef: MutableRefObject<boolean>,
|
|
510
517
|
autoSave?: boolean,
|
|
511
|
-
entityActions: EntityAction[]
|
|
518
|
+
entityActions: EntityAction[],
|
|
512
519
|
}) {
|
|
513
520
|
|
|
514
521
|
const {
|
|
@@ -530,7 +537,7 @@ function InnerForm<M extends Record<string, any>>(props: FormexController<M> & {
|
|
|
530
537
|
dirty,
|
|
531
538
|
closeAfterSaveRef,
|
|
532
539
|
autoSave,
|
|
533
|
-
entityActions
|
|
540
|
+
entityActions,
|
|
534
541
|
} = props;
|
|
535
542
|
|
|
536
543
|
const context = useFireCMSContext();
|
|
@@ -598,6 +605,7 @@ function InnerForm<M extends Record<string, any>>(props: FormexController<M> & {
|
|
|
598
605
|
<Tooltip title={<PropertyIdCopyTooltipContent propertyId={key}/>}
|
|
599
606
|
delayDuration={800}
|
|
600
607
|
side={"left"}
|
|
608
|
+
align={"start"}
|
|
601
609
|
sideOffset={16}>
|
|
602
610
|
<PropertyFieldBinding {...cmsFormFieldProps}/>
|
|
603
611
|
</Tooltip>
|
|
@@ -655,7 +663,7 @@ function InnerForm<M extends Record<string, any>>(props: FormexController<M> & {
|
|
|
655
663
|
fullPath: resolvedCollection.path,
|
|
656
664
|
collection: resolvedCollection,
|
|
657
665
|
context,
|
|
658
|
-
sideEntityController
|
|
666
|
+
sideEntityController,
|
|
659
667
|
});
|
|
660
668
|
}}>
|
|
661
669
|
{action.icon}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
|
|
3
|
-
import { Entity, ResolvedStringProperty } from "../../types";
|
|
3
|
+
import { Entity, EntityCollection, ResolvedStringProperty } from "../../types";
|
|
4
4
|
import { PreviewSize, PropertyPreview } from "../../preview";
|
|
5
5
|
|
|
6
6
|
import { cn, IconButton, paperMixin, RemoveIcon, Tooltip } from "@firecms/ui";
|
|
@@ -14,6 +14,7 @@ interface StorageItemPreviewProps {
|
|
|
14
14
|
onRemove: (value: string) => void;
|
|
15
15
|
size: PreviewSize;
|
|
16
16
|
disabled: boolean;
|
|
17
|
+
collection: EntityCollection;
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
export function StorageItemPreview({
|
|
@@ -23,7 +24,8 @@ export function StorageItemPreview({
|
|
|
23
24
|
entity,
|
|
24
25
|
onRemove,
|
|
25
26
|
disabled,
|
|
26
|
-
size
|
|
27
|
+
size,
|
|
28
|
+
collection
|
|
27
29
|
}: StorageItemPreviewProps) {
|
|
28
30
|
|
|
29
31
|
return (
|
|
@@ -54,7 +56,7 @@ export function StorageItemPreview({
|
|
|
54
56
|
<PropertyPreview propertyKey={name}
|
|
55
57
|
value={value}
|
|
56
58
|
property={property}
|
|
57
|
-
|
|
59
|
+
// entity={entity}
|
|
58
60
|
size={size}/>
|
|
59
61
|
</ErrorBoundary>
|
|
60
62
|
}
|
|
@@ -4,6 +4,7 @@ import { useSnackbarController, useStorageSource } from "../../hooks";
|
|
|
4
4
|
import { StorageFieldItem } from "../../util/useStorageUploadController";
|
|
5
5
|
import { ErrorView } from "../../components";
|
|
6
6
|
import { cn, paperMixin, Skeleton } from "@firecms/ui";
|
|
7
|
+
import { EntityCollection, StorageSource } from "../../types";
|
|
7
8
|
|
|
8
9
|
export interface StorageUploadItemProps {
|
|
9
10
|
storagePath: string;
|
|
@@ -22,10 +23,10 @@ export function StorageUploadProgress({
|
|
|
22
23
|
metadata,
|
|
23
24
|
onFileUploadComplete,
|
|
24
25
|
imageSize,
|
|
25
|
-
simple
|
|
26
|
+
simple,
|
|
26
27
|
}: StorageUploadItemProps) {
|
|
27
28
|
|
|
28
|
-
const
|
|
29
|
+
const storageSource = useStorageSource();
|
|
29
30
|
|
|
30
31
|
const snackbarController = useSnackbarController();
|
|
31
32
|
|
|
@@ -41,7 +42,7 @@ export function StorageUploadProgress({
|
|
|
41
42
|
setError(undefined);
|
|
42
43
|
setLoading(true);
|
|
43
44
|
|
|
44
|
-
|
|
45
|
+
storageSource.uploadFile({
|
|
45
46
|
file,
|
|
46
47
|
fileName,
|
|
47
48
|
path: storagePath,
|
|
@@ -67,7 +68,7 @@ export function StorageUploadProgress({
|
|
|
67
68
|
.finally(() => {
|
|
68
69
|
uploading.current = false;
|
|
69
70
|
});
|
|
70
|
-
}, [entry, metadata, onFileUploadComplete,
|
|
71
|
+
}, [entry, metadata, onFileUploadComplete, storageSource, storagePath]);
|
|
71
72
|
|
|
72
73
|
React.useEffect(() => {
|
|
73
74
|
mounted.current = true;
|
|
@@ -89,7 +90,7 @@ export function StorageUploadProgress({
|
|
|
89
90
|
|
|
90
91
|
<div className={cn(paperMixin,
|
|
91
92
|
"relative m-4 border-box flex items-center justify-center",
|
|
92
|
-
|
|
93
|
+
`min-w-[${imageSize}px] min-h-[${imageSize}px]`)}>
|
|
93
94
|
|
|
94
95
|
{loading &&
|
|
95
96
|
<Skeleton className="w-full h-full"/>}
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { FieldProps } from "../../types";
|
|
3
|
-
import { FieldHelperText } from "../components";
|
|
3
|
+
import { FieldHelperText, LabelWithIcon } from "../components";
|
|
4
4
|
import { PropertyFieldBinding } from "../PropertyFieldBinding";
|
|
5
|
-
import { useClearRestoreValue } from "../../hooks";
|
|
6
5
|
import { ExpandablePanel } from "@firecms/ui";
|
|
7
6
|
import { getIconForProperty } from "../../util";
|
|
8
|
-
import {
|
|
7
|
+
import { useClearRestoreValue } from "../useClearRestoreValue";
|
|
9
8
|
|
|
10
9
|
/**
|
|
11
10
|
* Array field used for custom
|