@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.
Files changed (195) hide show
  1. package/README.md +2 -2
  2. package/dist/components/ClearFilterSortButton.d.ts +5 -0
  3. package/dist/components/EntityCollectionTable/EntityCollectionRowActions.d.ts +11 -11
  4. package/dist/components/EntityCollectionTable/EntityCollectionTable.d.ts +2 -2
  5. package/dist/components/EntityCollectionTable/PropertyTableCell.d.ts +2 -2
  6. package/dist/components/EntityCollectionTable/internal/CollectionTableToolbar.d.ts +1 -4
  7. package/dist/components/EntityCollectionView/EntityCollectionView.d.ts +12 -3
  8. package/dist/components/EntityCollectionView/EntityCollectionViewStartActions.d.ts +11 -0
  9. package/dist/components/EntityCollectionView/useSelectionController.d.ts +2 -0
  10. package/dist/components/EntityPreview.d.ts +25 -7
  11. package/dist/components/EntityView.d.ts +11 -0
  12. package/dist/components/FieldCaption.d.ts +5 -0
  13. package/dist/components/HomePage/NavigationCard.d.ts +8 -0
  14. package/dist/components/HomePage/{NavigationCollectionCard.d.ts → NavigationCardBinding.d.ts} +2 -2
  15. package/dist/components/HomePage/SmallNavigationCard.d.ts +6 -0
  16. package/dist/components/HomePage/index.d.ts +3 -1
  17. package/dist/components/VirtualTable/VirtualTableProps.d.ts +1 -1
  18. package/dist/components/index.d.ts +4 -3
  19. package/dist/contexts/AuthControllerContext.d.ts +1 -1
  20. package/dist/{internal/EntityView.d.ts → core/EntityEditView.d.ts} +2 -2
  21. package/dist/core/SideEntityView.d.ts +7 -0
  22. package/dist/core/index.d.ts +0 -2
  23. package/dist/form/EntityForm.d.ts +1 -1
  24. package/dist/form/components/ErrorFocus.d.ts +1 -1
  25. package/dist/form/components/StorageItemPreview.d.ts +3 -2
  26. package/dist/form/components/StorageUploadProgress.d.ts +1 -1
  27. package/dist/form/components/index.d.ts +1 -0
  28. package/dist/form/field_bindings/KeyValueFieldBinding.d.ts +1 -1
  29. package/dist/form/field_bindings/MapFieldBinding.d.ts +1 -1
  30. package/dist/form/field_bindings/StorageUploadFieldBinding.d.ts +4 -3
  31. package/dist/form/field_bindings/TextFieldBinding.d.ts +2 -2
  32. package/dist/form/index.d.ts +1 -0
  33. package/dist/form/validation.d.ts +1 -1
  34. package/dist/hooks/data/delete.d.ts +2 -2
  35. package/dist/hooks/data/save.d.ts +1 -1
  36. package/dist/hooks/data/useDataSource.d.ts +2 -2
  37. package/dist/hooks/data/useEntityFetch.d.ts +3 -3
  38. package/dist/hooks/index.d.ts +3 -1
  39. package/dist/{core → hooks}/useBuildModeController.d.ts +1 -1
  40. package/dist/hooks/useBuildNavigationController.d.ts +6 -4
  41. package/dist/hooks/useProjectLog.d.ts +6 -2
  42. package/dist/hooks/useStorageSource.d.ts +2 -2
  43. package/dist/hooks/useValidateAuthenticator.d.ts +25 -0
  44. package/dist/index.es.js +8258 -7767
  45. package/dist/index.es.js.map +1 -1
  46. package/dist/index.umd.js +5 -5
  47. package/dist/index.umd.js.map +1 -1
  48. package/dist/internal/useBuildDataSource.d.ts +4 -0
  49. package/dist/preview/PropertyPreview.d.ts +1 -1
  50. package/dist/preview/PropertyPreviewProps.d.ts +1 -4
  51. package/dist/preview/components/BooleanPreview.d.ts +5 -1
  52. package/dist/preview/components/EnumValuesChip.d.ts +1 -1
  53. package/dist/preview/components/ReferencePreview.d.ts +1 -7
  54. package/dist/types/analytics.d.ts +1 -1
  55. package/dist/types/auth.d.ts +37 -1
  56. package/dist/types/collections.d.ts +22 -5
  57. package/dist/types/datasource.d.ts +1 -1
  58. package/dist/types/entities.d.ts +1 -1
  59. package/dist/types/entity_callbacks.d.ts +2 -2
  60. package/dist/types/entity_overrides.d.ts +6 -0
  61. package/dist/types/index.d.ts +2 -0
  62. package/dist/types/navigation.d.ts +14 -13
  63. package/dist/types/permissions.d.ts +5 -1
  64. package/dist/types/plugins.d.ts +20 -20
  65. package/dist/types/properties.d.ts +2 -2
  66. package/dist/types/property_config.d.ts +2 -2
  67. package/dist/types/roles.d.ts +31 -0
  68. package/dist/types/storage.d.ts +11 -3
  69. package/dist/types/user.d.ts +5 -0
  70. package/dist/util/collections.d.ts +9 -1
  71. package/dist/util/entities.d.ts +1 -1
  72. package/dist/util/icons.d.ts +8 -2
  73. package/dist/util/navigation_utils.d.ts +2 -2
  74. package/dist/util/permissions.d.ts +4 -4
  75. package/dist/util/references.d.ts +4 -2
  76. package/dist/util/resolutions.d.ts +6 -6
  77. package/dist/util/useTraceUpdate.d.ts +1 -0
  78. package/package.json +27 -24
  79. package/src/components/ClearFilterSortButton.tsx +41 -0
  80. package/src/components/DeleteEntityDialog.tsx +4 -4
  81. package/src/components/EntityCollectionTable/EntityCollectionRowActions.tsx +2 -2
  82. package/src/components/EntityCollectionTable/EntityCollectionTable.tsx +268 -277
  83. package/src/components/EntityCollectionTable/EntityCollectionTableProps.tsx +1 -1
  84. package/src/components/EntityCollectionTable/PropertyTableCell.tsx +13 -13
  85. package/src/components/EntityCollectionTable/fields/TableReferenceField.tsx +9 -16
  86. package/src/components/EntityCollectionTable/fields/TableStorageUpload.tsx +3 -3
  87. package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +27 -32
  88. package/src/components/EntityCollectionTable/internal/default_entity_actions.tsx +9 -5
  89. package/src/components/EntityCollectionView/EntityCollectionView.tsx +51 -52
  90. package/src/components/EntityCollectionView/EntityCollectionViewActions.tsx +5 -6
  91. package/src/components/EntityCollectionView/EntityCollectionViewStartActions.tsx +68 -0
  92. package/src/components/EntityCollectionView/useSelectionController.tsx +30 -0
  93. package/src/components/EntityPreview.tsx +207 -70
  94. package/src/components/EntityView.tsx +84 -0
  95. package/src/components/FieldCaption.tsx +14 -0
  96. package/src/components/FireCMSAppBar.tsx +8 -0
  97. package/src/components/HomePage/DefaultHomePage.tsx +14 -10
  98. package/src/components/HomePage/NavigationCard.tsx +69 -0
  99. package/src/components/HomePage/NavigationCardBinding.tsx +116 -0
  100. package/src/components/HomePage/SmallNavigationCard.tsx +45 -0
  101. package/src/components/HomePage/index.tsx +3 -1
  102. package/src/components/ReferenceTable/ReferenceSelectionTable.tsx +3 -4
  103. package/src/components/ReferenceWidget.tsx +4 -4
  104. package/src/components/SelectableTable/filters/DateTimeFilterField.tsx +23 -8
  105. package/src/components/SelectableTable/filters/ReferenceFilterField.tsx +35 -24
  106. package/src/components/SelectableTable/filters/StringNumberFilterField.tsx +35 -15
  107. package/src/components/VirtualTable/VirtualTable.tsx +17 -7
  108. package/src/components/VirtualTable/VirtualTableProps.tsx +1 -1
  109. package/src/components/VirtualTable/fields/VirtualTableDateField.tsx +1 -1
  110. package/src/components/common/useDataSourceEntityCollectionTableController.tsx +1 -1
  111. package/src/components/index.tsx +4 -3
  112. package/src/contexts/AuthControllerContext.tsx +1 -1
  113. package/src/core/Drawer.tsx +66 -39
  114. package/src/{internal/EntityView.tsx → core/EntityEditView.tsx} +22 -39
  115. package/src/core/EntitySidePanel.tsx +2 -2
  116. package/src/core/FireCMS.tsx +18 -3
  117. package/src/core/NavigationRoutes.tsx +8 -0
  118. package/src/core/SideEntityView.tsx +38 -0
  119. package/src/core/field_configs.tsx +1 -2
  120. package/src/core/index.tsx +0 -2
  121. package/src/form/EntityForm.tsx +20 -12
  122. package/src/form/components/StorageItemPreview.tsx +5 -3
  123. package/src/form/components/StorageUploadProgress.tsx +6 -5
  124. package/src/form/components/index.tsx +1 -0
  125. package/src/form/field_bindings/ArrayCustomShapedFieldBinding.tsx +2 -3
  126. package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +12 -15
  127. package/src/form/field_bindings/BlockFieldBinding.tsx +2 -3
  128. package/src/form/field_bindings/DateTimeFieldBinding.tsx +4 -4
  129. package/src/form/field_bindings/KeyValueFieldBinding.tsx +18 -18
  130. package/src/form/field_bindings/MapFieldBinding.tsx +17 -17
  131. package/src/form/field_bindings/MarkdownFieldBinding.tsx +1 -2
  132. package/src/form/field_bindings/MultiSelectBinding.tsx +2 -3
  133. package/src/form/field_bindings/ReadOnlyFieldBinding.tsx +3 -3
  134. package/src/form/field_bindings/ReferenceFieldBinding.tsx +5 -3
  135. package/src/form/field_bindings/RepeatFieldBinding.tsx +3 -3
  136. package/src/form/field_bindings/SelectFieldBinding.tsx +2 -3
  137. package/src/form/field_bindings/StorageUploadFieldBinding.tsx +15 -6
  138. package/src/form/field_bindings/SwitchFieldBinding.tsx +2 -3
  139. package/src/form/field_bindings/TextFieldBinding.tsx +10 -9
  140. package/src/form/index.tsx +1 -0
  141. package/src/form/validation.ts +3 -4
  142. package/src/hooks/data/delete.ts +3 -3
  143. package/src/hooks/data/save.ts +1 -1
  144. package/src/hooks/data/useCollectionFetch.tsx +1 -1
  145. package/src/hooks/data/useDataSource.tsx +8 -3
  146. package/src/hooks/data/useEntityFetch.tsx +4 -4
  147. package/src/hooks/index.tsx +5 -1
  148. package/src/{core → hooks}/useBuildLocalConfigurationPersistence.tsx +9 -10
  149. package/src/{core → hooks}/useBuildModeController.tsx +12 -6
  150. package/src/hooks/useBuildNavigationController.tsx +197 -79
  151. package/src/hooks/useProjectLog.tsx +17 -7
  152. package/src/hooks/useReferenceDialog.tsx +2 -2
  153. package/src/hooks/useStorageSource.tsx +7 -2
  154. package/src/hooks/useValidateAuthenticator.tsx +135 -0
  155. package/src/internal/useBuildDataSource.ts +6 -1
  156. package/src/internal/useBuildSideEntityController.tsx +18 -12
  157. package/src/preview/PropertyPreview.tsx +1 -1
  158. package/src/preview/PropertyPreviewProps.tsx +1 -11
  159. package/src/preview/components/BooleanPreview.tsx +19 -4
  160. package/src/preview/components/EnumValuesChip.tsx +1 -1
  161. package/src/preview/components/ReferencePreview.tsx +55 -147
  162. package/src/preview/property_previews/StringPropertyPreview.tsx +8 -7
  163. package/src/types/analytics.ts +1 -0
  164. package/src/types/auth.tsx +50 -1
  165. package/src/types/collections.ts +24 -5
  166. package/src/types/datasource.ts +1 -1
  167. package/src/types/entities.ts +1 -1
  168. package/src/types/entity_actions.tsx +4 -0
  169. package/src/types/entity_callbacks.ts +2 -2
  170. package/src/types/entity_overrides.tsx +7 -0
  171. package/src/types/firecms.tsx +0 -1
  172. package/src/types/index.ts +2 -0
  173. package/src/types/navigation.ts +17 -16
  174. package/src/types/permissions.ts +6 -1
  175. package/src/types/plugins.tsx +26 -28
  176. package/src/types/properties.ts +3 -2
  177. package/src/types/property_config.tsx +2 -2
  178. package/src/types/roles.ts +41 -0
  179. package/src/types/side_entity_controller.tsx +1 -0
  180. package/src/types/storage.ts +12 -3
  181. package/src/types/user.ts +7 -0
  182. package/src/util/collections.ts +22 -0
  183. package/src/util/entities.ts +1 -1
  184. package/src/util/icons.tsx +11 -3
  185. package/src/util/navigation_utils.ts +6 -6
  186. package/src/util/permissions.ts +11 -8
  187. package/src/util/references.ts +36 -5
  188. package/src/util/strings.ts +2 -2
  189. package/src/util/useTraceUpdate.tsx +2 -1
  190. package/dist/internal/useLocaleConfig.d.ts +0 -1
  191. package/src/components/HomePage/NavigationCollectionCard.tsx +0 -146
  192. package/src/internal/useLocaleConfig.tsx +0 -18
  193. /package/dist/{components → form/components}/LabelWithIcon.d.ts +0 -0
  194. /package/dist/{core → hooks}/useBuildLocalConfigurationPersistence.d.ts +0 -0
  195. /package/src/{components → form/components}/LabelWithIcon.tsx +0 -0
@@ -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 ungroupedNavigationViews = Object.values(navigationEntries).filter(e => !e.group);
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
- <div className={"flex-grow overflow-scroll no-scrollbar"}>
71
-
72
- {groups.map((group) => (
73
- <React.Fragment
74
- key={`drawer_group_${group}`}>
75
- {buildGroupHeader(group)}
76
- {Object.values(navigationEntries)
77
- .filter(e => e.group === group)
78
- .map((view, index) =>
79
- <DrawerNavigationItem
80
- key={`navigation_${index}`}
81
- icon={<IconForView collectionOrView={view.collection ?? view.view}/>}
82
- tooltipsOpen={tooltipsOpen}
83
- drawerOpen={drawerOpen}
84
- onClick={() => onClick(view)}
85
- url={view.url}
86
- name={view.name}/>)}
87
- </React.Fragment>
88
- ))}
89
-
90
- {ungroupedNavigationViews.length > 0 && buildGroupHeader()}
91
-
92
- {ungroupedNavigationViews.map((view, index) => {
93
-
94
- return <DrawerNavigationItem
95
- key={`navigation_${index}`}
96
- icon={<IconForView collectionOrView={view.collection ?? view.view}/>}
97
- tooltipsOpen={tooltipsOpen}
98
- onClick={() => onClick(view)}
99
- drawerOpen={drawerOpen}
100
- url={view.url}
101
- name={view.name}/>;
102
- })}
103
-
104
- </div>
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-gray-100",
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, EntityPreview, ErrorBoundary, } from "../components";
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 "./common";
36
- import { useSideDialogContext } from "../core";
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 EntityViewProps<M extends Record<string, any>> {
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 EntityView<M extends Record<string, any>, UserType extends User>({
60
- path,
61
- entityId,
62
- selectedSubPath,
63
- copy,
64
- collection,
65
- parentCollectionIds,
66
- onValuesAreModified,
67
- formWidth,
68
- onUpdate,
69
- onClose
70
- }: EntityViewProps<M>) {
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, fullPathToCollectionSegments(path), usedEntity ?? null) : false;
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
- <EntityPreview
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 { EntityView } from "../internal/EntityView";
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
- <EntityView
80
+ <EntityEditView
81
81
  {...props}
82
82
  formWidth={props.width}
83
83
  collection={collection}
@@ -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 "../internal/useLocaleConfig";
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
- if (property.properties)
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;
@@ -10,5 +10,3 @@ export * from "./field_configs";
10
10
 
11
11
  export * from "./SideDialogs";
12
12
  export * from "./NavigationRoutes";
13
- export * from "./useBuildModeController";
14
- export * from "./useBuildLocalConfigurationPersistence";
@@ -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(({ entity, customEntityActions }: {
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, fullPathToCollectionSegments(path), null);
397
- const deleteEnabled = entity ? canDeleteEntity(inputCollection, authController, fullPathToCollectionSegments(path), entity) : true;
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.name}`} {...actionProps}/>
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
- // entity={entity}
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 storage = useStorageSource();
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
- storage.uploadFile({
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, storage, storagePath]);
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
- `min-w-[${imageSize}px] min-h-[${imageSize}px]`)}>
93
+ `min-w-[${imageSize}px] min-h-[${imageSize}px]`)}>
93
94
 
94
95
  {loading &&
95
96
  <Skeleton className="w-full h-full"/>}
@@ -1,2 +1,3 @@
1
1
  export * from "./FormikArrayContainer";
2
2
  export * from "./FieldHelperText";
3
+ export * from "./LabelWithIcon";
@@ -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 { LabelWithIcon } from "../../components";
7
+ import { useClearRestoreValue } from "../useClearRestoreValue";
9
8
 
10
9
  /**
11
10
  * Array field used for custom