@firecms/core 3.0.0-canary.1 → 3.0.0-canary.100

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 (301) hide show
  1. package/README.md +2 -2
  2. package/dist/app/AppBar.d.ts +12 -0
  3. package/dist/app/Drawer.d.ts +17 -0
  4. package/dist/app/Scaffold.d.ts +30 -0
  5. package/dist/app/index.d.ts +4 -0
  6. package/dist/app/useApp.d.ts +16 -0
  7. package/dist/components/CircularProgressCenter.d.ts +1 -1
  8. package/dist/components/ClearFilterSortButton.d.ts +5 -0
  9. package/dist/components/EntityCollectionTable/EntityCollectionRowActions.d.ts +11 -12
  10. package/dist/components/EntityCollectionTable/EntityCollectionTable.d.ts +2 -2
  11. package/dist/components/EntityCollectionTable/EntityCollectionTableProps.d.ts +5 -3
  12. package/dist/components/EntityCollectionTable/PropertyTableCell.d.ts +3 -2
  13. package/dist/components/EntityCollectionTable/column_utils.d.ts +1 -2
  14. package/dist/components/EntityCollectionTable/fields/TableReferenceField.d.ts +2 -0
  15. package/dist/components/EntityCollectionTable/internal/CollectionTableToolbar.d.ts +1 -4
  16. package/dist/components/EntityCollectionTable/internal/EntityTableCell.d.ts +2 -2
  17. package/dist/components/EntityCollectionTable/internal/popup_field/PopupFormField.d.ts +1 -1
  18. package/dist/components/EntityCollectionView/EntityCollectionView.d.ts +13 -4
  19. package/dist/components/EntityCollectionView/EntityCollectionViewStartActions.d.ts +11 -0
  20. package/dist/components/EntityCollectionView/useSelectionController.d.ts +2 -0
  21. package/dist/components/EntityPreview.d.ts +26 -7
  22. package/dist/components/EntityView.d.ts +11 -0
  23. package/dist/components/ErrorView.d.ts +1 -1
  24. package/dist/components/FieldCaption.d.ts +5 -0
  25. package/dist/components/HomePage/DefaultHomePage.d.ts +1 -1
  26. package/dist/components/HomePage/NavigationCard.d.ts +8 -0
  27. package/dist/components/HomePage/{NavigationCollectionCard.d.ts → NavigationCardBinding.d.ts} +3 -3
  28. package/dist/components/HomePage/SmallNavigationCard.d.ts +6 -0
  29. package/dist/components/HomePage/index.d.ts +3 -1
  30. package/dist/components/ReferenceWidget.d.ts +6 -4
  31. package/dist/components/SelectableTable/SelectableTable.d.ts +1 -1
  32. package/dist/components/SelectableTable/filters/ReferenceFilterField.d.ts +2 -1
  33. package/dist/components/VirtualTable/VirtualTableProps.d.ts +16 -13
  34. package/dist/components/VirtualTable/types.d.ts +3 -3
  35. package/dist/components/{EntityCollectionTable/internal → common}/default_entity_actions.d.ts +1 -1
  36. package/dist/components/common/index.d.ts +1 -0
  37. package/dist/components/common/table_height.d.ts +5 -0
  38. package/dist/components/common/types.d.ts +4 -6
  39. package/dist/components/common/useColumnsIds.d.ts +2 -1
  40. package/dist/components/common/useDataSourceEntityCollectionTableController.d.ts +3 -0
  41. package/dist/components/index.d.ts +6 -4
  42. package/dist/contexts/AuthControllerContext.d.ts +1 -1
  43. package/dist/{components/FireCMSAppBar.d.ts → core/DefaultAppBar.d.ts} +6 -9
  44. package/dist/core/DefaultDrawer.d.ts +19 -0
  45. package/dist/core/DrawerNavigationItem.d.ts +9 -0
  46. package/dist/core/EntityEditView.d.ts +36 -0
  47. package/dist/core/FireCMS.d.ts +1 -1
  48. package/dist/core/NavigationRoutes.d.ts +3 -3
  49. package/dist/core/index.d.ts +3 -6
  50. package/dist/form/PropertiesForm.d.ts +8 -0
  51. package/dist/form/PropertyFieldBinding.d.ts +1 -1
  52. package/dist/form/components/ErrorFocus.d.ts +1 -1
  53. package/dist/form/components/FieldHelperText.d.ts +3 -3
  54. package/dist/form/components/StorageItemPreview.d.ts +4 -3
  55. package/dist/form/components/StorageUploadProgress.d.ts +1 -1
  56. package/dist/form/components/index.d.ts +1 -0
  57. package/dist/form/field_bindings/KeyValueFieldBinding.d.ts +1 -1
  58. package/dist/form/field_bindings/MapFieldBinding.d.ts +1 -1
  59. package/dist/form/field_bindings/StorageUploadFieldBinding.d.ts +3 -4
  60. package/dist/form/field_bindings/TextFieldBinding.d.ts +2 -2
  61. package/dist/form/index.d.ts +1 -2
  62. package/dist/form/validation.d.ts +1 -1
  63. package/dist/hooks/data/delete.d.ts +2 -2
  64. package/dist/hooks/data/save.d.ts +2 -3
  65. package/dist/hooks/data/useDataSource.d.ts +2 -2
  66. package/dist/hooks/data/useEntityFetch.d.ts +3 -3
  67. package/dist/hooks/index.d.ts +4 -1
  68. package/dist/{core → hooks}/useBuildModeController.d.ts +1 -1
  69. package/dist/hooks/useBuildNavigationController.d.ts +6 -4
  70. package/dist/hooks/useProjectLog.d.ts +6 -2
  71. package/dist/hooks/useStorageSource.d.ts +2 -2
  72. package/dist/hooks/useValidateAuthenticator.d.ts +21 -0
  73. package/dist/index.d.ts +1 -0
  74. package/dist/index.es.js +16400 -12423
  75. package/dist/index.es.js.map +1 -1
  76. package/dist/index.umd.js +19713 -7
  77. package/dist/index.umd.js.map +1 -1
  78. package/dist/internal/useBuildDataSource.d.ts +1 -12
  79. package/dist/preview/PropertyPreview.d.ts +1 -1
  80. package/dist/preview/PropertyPreviewProps.d.ts +6 -4
  81. package/dist/preview/components/BooleanPreview.d.ts +5 -1
  82. package/dist/preview/components/EnumValuesChip.d.ts +1 -1
  83. package/dist/preview/components/ReferencePreview.d.ts +3 -8
  84. package/dist/preview/components/StorageThumbnail.d.ts +2 -1
  85. package/dist/preview/components/UrlComponentPreview.d.ts +2 -1
  86. package/dist/types/analytics.d.ts +1 -1
  87. package/dist/types/auth.d.ts +38 -2
  88. package/dist/types/collections.d.ts +45 -8
  89. package/dist/types/datasource.d.ts +34 -20
  90. package/dist/types/entities.d.ts +6 -1
  91. package/dist/types/entity_actions.d.ts +14 -0
  92. package/dist/types/entity_callbacks.d.ts +2 -2
  93. package/dist/types/entity_overrides.d.ts +6 -0
  94. package/dist/types/fields.d.ts +36 -30
  95. package/dist/types/index.d.ts +2 -1
  96. package/dist/types/navigation.d.ts +15 -14
  97. package/dist/types/permissions.d.ts +5 -1
  98. package/dist/types/plugins.d.ts +31 -23
  99. package/dist/types/properties.d.ts +34 -21
  100. package/dist/types/property_config.d.ts +2 -2
  101. package/dist/types/roles.d.ts +31 -0
  102. package/dist/types/storage.d.ts +11 -3
  103. package/dist/types/user.d.ts +5 -0
  104. package/dist/util/collections.d.ts +9 -1
  105. package/dist/util/entities.d.ts +1 -1
  106. package/dist/util/icon_list.d.ts +5 -1
  107. package/dist/util/icon_synonyms.d.ts +1 -97
  108. package/dist/util/icons.d.ts +9 -3
  109. package/dist/util/index.d.ts +1 -0
  110. package/dist/util/navigation_utils.d.ts +2 -2
  111. package/dist/util/objects.d.ts +1 -1
  112. package/dist/util/permissions.d.ts +4 -4
  113. package/dist/util/plurals.d.ts +0 -2
  114. package/dist/util/references.d.ts +4 -2
  115. package/dist/util/resolutions.d.ts +15 -14
  116. package/dist/util/storage.d.ts +23 -2
  117. package/dist/util/useStorageUploadController.d.ts +1 -1
  118. package/dist/util/useTraceUpdate.d.ts +1 -0
  119. package/package.json +128 -119
  120. package/src/app/AppBar.tsx +18 -0
  121. package/src/app/Drawer.tsx +25 -0
  122. package/src/app/Scaffold.tsx +251 -0
  123. package/src/app/index.ts +4 -0
  124. package/src/app/useApp.tsx +32 -0
  125. package/src/components/ArrayContainer.tsx +1 -1
  126. package/src/components/CircularProgressCenter.tsx +1 -1
  127. package/src/components/ClearFilterSortButton.tsx +41 -0
  128. package/src/components/DeleteEntityDialog.tsx +4 -4
  129. package/src/components/EntityCollectionTable/EntityCollectionRowActions.tsx +11 -21
  130. package/src/components/EntityCollectionTable/EntityCollectionTable.tsx +276 -279
  131. package/src/components/EntityCollectionTable/EntityCollectionTableProps.tsx +9 -5
  132. package/src/components/EntityCollectionTable/PropertyTableCell.tsx +49 -45
  133. package/src/components/EntityCollectionTable/column_utils.tsx +3 -3
  134. package/src/components/EntityCollectionTable/fields/TableReferenceField.tsx +20 -18
  135. package/src/components/EntityCollectionTable/fields/TableStorageUpload.tsx +17 -9
  136. package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +29 -34
  137. package/src/components/EntityCollectionTable/internal/EntityTableCell.tsx +16 -12
  138. package/src/components/EntityCollectionTable/internal/popup_field/PopupFormField.tsx +4 -5
  139. package/src/components/EntityCollectionView/EntityCollectionView.tsx +95 -92
  140. package/src/components/EntityCollectionView/EntityCollectionViewActions.tsx +5 -6
  141. package/src/components/EntityCollectionView/EntityCollectionViewStartActions.tsx +68 -0
  142. package/src/components/EntityCollectionView/useSelectionController.tsx +42 -0
  143. package/src/components/EntityPreview.tsx +210 -70
  144. package/src/components/EntityView.tsx +84 -0
  145. package/src/components/ErrorView.tsx +1 -1
  146. package/src/components/FieldCaption.tsx +14 -0
  147. package/src/components/HomePage/DefaultHomePage.tsx +37 -24
  148. package/src/components/HomePage/NavigationCard.tsx +69 -0
  149. package/src/components/HomePage/NavigationCardBinding.tsx +116 -0
  150. package/src/components/HomePage/SmallNavigationCard.tsx +45 -0
  151. package/src/components/HomePage/index.tsx +3 -1
  152. package/src/components/PropertyIdCopyTooltipContent.tsx +2 -3
  153. package/src/components/ReferenceTable/ReferenceSelectionTable.tsx +4 -4
  154. package/src/components/ReferenceWidget.tsx +26 -16
  155. package/src/components/SearchIconsView.tsx +9 -6
  156. package/src/components/SelectableTable/SelectableTable.tsx +7 -7
  157. package/src/components/SelectableTable/filters/BooleanFilterField.tsx +2 -3
  158. package/src/components/SelectableTable/filters/DateTimeFilterField.tsx +22 -7
  159. package/src/components/SelectableTable/filters/ReferenceFilterField.tsx +38 -24
  160. package/src/components/SelectableTable/filters/StringNumberFilterField.tsx +35 -15
  161. package/src/components/VirtualTable/VirtualTable.tsx +70 -37
  162. package/src/components/VirtualTable/VirtualTableCell.tsx +1 -1
  163. package/src/components/VirtualTable/VirtualTableHeader.tsx +4 -4
  164. package/src/components/VirtualTable/VirtualTableHeaderRow.tsx +2 -2
  165. package/src/components/VirtualTable/VirtualTableProps.tsx +19 -15
  166. package/src/components/VirtualTable/VirtualTableRow.tsx +4 -5
  167. package/src/components/VirtualTable/fields/VirtualTableDateField.tsx +1 -1
  168. package/src/components/VirtualTable/types.tsx +2 -3
  169. package/src/components/{EntityCollectionTable/internal → common}/default_entity_actions.tsx +11 -7
  170. package/src/components/common/index.ts +1 -0
  171. package/src/components/{VirtualTable/common.tsx → common/table_height.tsx} +5 -2
  172. package/src/components/common/types.tsx +4 -6
  173. package/src/components/common/useColumnsIds.tsx +23 -2
  174. package/src/components/common/useDataSourceEntityCollectionTableController.tsx +12 -1
  175. package/src/components/common/useTableSearchHelper.ts +52 -12
  176. package/src/components/index.tsx +6 -4
  177. package/src/contexts/AuthControllerContext.tsx +1 -1
  178. package/src/contexts/DialogsProvider.tsx +2 -2
  179. package/src/{components/FireCMSAppBar.tsx → core/DefaultAppBar.tsx} +60 -37
  180. package/src/core/DefaultDrawer.tsx +177 -0
  181. package/src/core/DrawerNavigationItem.tsx +62 -0
  182. package/src/core/EntityEditView.tsx +1130 -0
  183. package/src/core/EntitySidePanel.tsx +3 -4
  184. package/src/core/FireCMS.tsx +55 -44
  185. package/src/core/NavigationRoutes.tsx +15 -8
  186. package/src/core/SideDialogs.tsx +1 -0
  187. package/src/core/field_configs.tsx +2 -3
  188. package/src/core/index.tsx +3 -6
  189. package/src/form/PropertiesForm.tsx +81 -0
  190. package/src/form/PropertyFieldBinding.tsx +38 -9
  191. package/src/form/components/FieldHelperText.tsx +3 -3
  192. package/src/form/components/StorageItemPreview.tsx +21 -10
  193. package/src/form/components/StorageUploadProgress.tsx +9 -8
  194. package/src/form/components/index.tsx +1 -0
  195. package/src/form/field_bindings/ArrayCustomShapedFieldBinding.tsx +10 -8
  196. package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +18 -18
  197. package/src/form/field_bindings/BlockFieldBinding.tsx +4 -5
  198. package/src/form/field_bindings/DateTimeFieldBinding.tsx +3 -3
  199. package/src/form/field_bindings/KeyValueFieldBinding.tsx +62 -57
  200. package/src/form/field_bindings/MapFieldBinding.tsx +28 -20
  201. package/src/form/field_bindings/MarkdownFieldBinding.tsx +3 -4
  202. package/src/form/field_bindings/MultiSelectBinding.tsx +2 -3
  203. package/src/form/field_bindings/ReadOnlyFieldBinding.tsx +4 -11
  204. package/src/form/field_bindings/ReferenceFieldBinding.tsx +21 -17
  205. package/src/form/field_bindings/RepeatFieldBinding.tsx +14 -11
  206. package/src/form/field_bindings/SelectFieldBinding.tsx +5 -6
  207. package/src/form/field_bindings/StorageUploadFieldBinding.tsx +28 -40
  208. package/src/form/field_bindings/SwitchFieldBinding.tsx +3 -4
  209. package/src/form/field_bindings/TextFieldBinding.tsx +10 -9
  210. package/src/form/index.tsx +5 -4
  211. package/src/form/validation.ts +4 -21
  212. package/src/hooks/data/delete.ts +3 -3
  213. package/src/hooks/data/save.ts +4 -2
  214. package/src/hooks/data/useCollectionFetch.tsx +1 -1
  215. package/src/hooks/data/useDataSource.tsx +7 -2
  216. package/src/hooks/data/useEntityFetch.tsx +4 -4
  217. package/src/hooks/index.tsx +6 -1
  218. package/src/{core → hooks}/useBuildLocalConfigurationPersistence.tsx +8 -10
  219. package/src/{core → hooks}/useBuildModeController.tsx +12 -6
  220. package/src/hooks/useBuildNavigationController.tsx +208 -92
  221. package/src/hooks/useProjectLog.tsx +30 -9
  222. package/src/hooks/useReferenceDialog.tsx +2 -2
  223. package/src/hooks/useResolvedNavigationFrom.tsx +1 -1
  224. package/src/hooks/useStorageSource.tsx +7 -2
  225. package/src/hooks/useValidateAuthenticator.tsx +115 -0
  226. package/src/index.ts +1 -0
  227. package/src/internal/useBuildDataSource.ts +56 -44
  228. package/src/internal/useBuildSideEntityController.tsx +88 -21
  229. package/src/preview/PropertyPreview.tsx +11 -18
  230. package/src/preview/PropertyPreviewProps.tsx +4 -8
  231. package/src/preview/components/BooleanPreview.tsx +19 -4
  232. package/src/preview/components/EnumValuesChip.tsx +2 -2
  233. package/src/preview/components/ImagePreview.tsx +21 -33
  234. package/src/preview/components/ReferencePreview.tsx +74 -166
  235. package/src/preview/components/StorageThumbnail.tsx +5 -1
  236. package/src/preview/components/UrlComponentPreview.tsx +44 -11
  237. package/src/preview/property_previews/ArrayOfMapsPreview.tsx +0 -1
  238. package/src/preview/property_previews/ArrayOfReferencesPreview.tsx +2 -1
  239. package/src/preview/property_previews/ArrayOfStorageComponentsPreview.tsx +0 -1
  240. package/src/preview/property_previews/ArrayOfStringsPreview.tsx +0 -1
  241. package/src/preview/property_previews/ArrayOneOfPreview.tsx +2 -3
  242. package/src/preview/property_previews/ArrayPropertyPreview.tsx +2 -3
  243. package/src/preview/property_previews/MapPropertyPreview.tsx +5 -5
  244. package/src/preview/property_previews/StringPropertyPreview.tsx +8 -7
  245. package/src/types/analytics.ts +1 -0
  246. package/src/types/auth.tsx +51 -2
  247. package/src/types/collections.ts +52 -8
  248. package/src/types/customization_controller.tsx +0 -1
  249. package/src/types/datasource.ts +41 -24
  250. package/src/types/entities.ts +13 -1
  251. package/src/types/entity_actions.tsx +17 -0
  252. package/src/types/entity_callbacks.ts +2 -2
  253. package/src/types/entity_overrides.tsx +7 -0
  254. package/src/types/fields.tsx +39 -33
  255. package/src/types/firecms.tsx +0 -1
  256. package/src/types/index.ts +2 -1
  257. package/src/types/navigation.ts +17 -17
  258. package/src/types/permissions.ts +6 -1
  259. package/src/types/plugins.tsx +38 -31
  260. package/src/types/properties.ts +40 -22
  261. package/src/types/property_config.tsx +2 -2
  262. package/src/types/roles.ts +41 -0
  263. package/src/types/side_entity_controller.tsx +1 -0
  264. package/src/types/storage.ts +12 -3
  265. package/src/types/user.ts +7 -0
  266. package/src/util/collections.ts +22 -0
  267. package/src/util/entities.ts +5 -4
  268. package/src/util/enums.ts +1 -1
  269. package/src/util/icon_list.ts +15 -10
  270. package/src/util/icon_synonyms.ts +3 -99
  271. package/src/util/icons.tsx +11 -3
  272. package/src/util/index.ts +1 -0
  273. package/src/util/navigation_utils.ts +6 -6
  274. package/src/util/objects.ts +25 -28
  275. package/src/util/permissions.ts +12 -8
  276. package/src/util/plurals.ts +0 -2
  277. package/src/util/references.ts +36 -5
  278. package/src/util/resolutions.ts +44 -39
  279. package/src/util/storage.ts +75 -21
  280. package/src/util/strings.ts +2 -2
  281. package/src/util/useStorageUploadController.tsx +21 -3
  282. package/src/util/useTraceUpdate.tsx +2 -1
  283. package/dist/components/VirtualTable/common.d.ts +0 -2
  284. package/dist/core/Drawer.d.ts +0 -23
  285. package/dist/core/Scaffold.d.ts +0 -55
  286. package/dist/form/EntityForm.d.ts +0 -77
  287. package/dist/internal/EntityView.d.ts +0 -22
  288. package/dist/internal/useBuildCustomizationController.d.ts +0 -2
  289. package/dist/internal/useLocaleConfig.d.ts +0 -1
  290. package/dist/types/appcheck.d.ts +0 -26
  291. package/src/components/HomePage/NavigationCollectionCard.tsx +0 -146
  292. package/src/core/Drawer.tsx +0 -164
  293. package/src/core/Scaffold.tsx +0 -281
  294. package/src/form/EntityForm.tsx +0 -720
  295. package/src/internal/EntityView.tsx +0 -578
  296. package/src/internal/useBuildCustomizationController.tsx +0 -5
  297. package/src/internal/useLocaleConfig.tsx +0 -18
  298. package/src/types/appcheck.ts +0 -29
  299. /package/dist/{components → form/components}/LabelWithIcon.d.ts +0 -0
  300. /package/dist/{core → hooks}/useBuildLocalConfigurationPersistence.d.ts +0 -0
  301. /package/src/{components → form/components}/LabelWithIcon.tsx +0 -0
@@ -1,5 +1,5 @@
1
- import { useCallback, useEffect, useMemo, useState } from "react";
2
- import { useLocation } from "react-router-dom";
1
+ import { useCallback, useEffect, useRef, useState } from "react";
2
+ import equal from "react-fast-compare"
3
3
 
4
4
  import {
5
5
  AuthController,
@@ -10,12 +10,14 @@ import {
10
10
  EntityCollectionsBuilder,
11
11
  EntityReference,
12
12
  NavigationController,
13
+ PermissionsBuilder,
13
14
  TopNavigationEntry,
14
15
  TopNavigationResult,
15
16
  User,
16
17
  UserConfigurationPersistence
17
18
  } from "../types";
18
19
  import {
20
+ applyPermissionsFunctionIfEmpty,
19
21
  getCollectionByPathOrId,
20
22
  mergeDeep,
21
23
  removeInitialAndTrailingSlashes,
@@ -27,12 +29,15 @@ import { getParentReferencesFromPath } from "../util/parent_references_from_path
27
29
  const DEFAULT_BASE_PATH = "/";
28
30
  const DEFAULT_COLLECTION_PATH = "/c";
29
31
 
30
- type BuildNavigationContextProps<EC extends EntityCollection, UserType extends User> = {
32
+ export type BuildNavigationContextProps<EC extends EntityCollection, UserType extends User> = {
31
33
  basePath?: string,
32
34
  baseCollectionPath?: string,
33
35
  authController: AuthController<UserType>;
34
36
  collections?: EC[] | EntityCollectionsBuilder<EC>;
37
+ collectionPermissions?: PermissionsBuilder;
35
38
  views?: CMSView[] | CMSViewsBuilder;
39
+ adminViews?: CMSView[] | CMSViewsBuilder;
40
+ viewsOrder?: string[];
36
41
  userConfigPersistence?: UserConfigurationPersistence;
37
42
  dataSourceDelegate: DataSourceDelegate;
38
43
  /**
@@ -45,21 +50,25 @@ type BuildNavigationContextProps<EC extends EntityCollection, UserType extends U
45
50
  injectCollections?: (collections: EntityCollection[]) => EntityCollection[];
46
51
  };
47
52
 
48
- export function useBuildNavigationController<EC extends EntityCollection, UserType extends User>({
49
- basePath = DEFAULT_BASE_PATH,
50
- baseCollectionPath = DEFAULT_COLLECTION_PATH,
51
- authController,
52
- collections: baseCollections,
53
- views: baseViews,
54
- userConfigPersistence,
55
- dataSourceDelegate,
56
- injectCollections
57
- }: BuildNavigationContextProps<EC, UserType>): NavigationController {
58
-
59
- const location = useLocation();
60
-
61
- const [collections, setCollections] = useState<EntityCollection[] | undefined>();
62
- const [views, setViews] = useState<CMSView[] | undefined>();
53
+ export function useBuildNavigationController<EC extends EntityCollection, UserType extends User>(props: BuildNavigationContextProps<EC, UserType>): NavigationController {
54
+ const {
55
+ basePath = DEFAULT_BASE_PATH,
56
+ baseCollectionPath = DEFAULT_COLLECTION_PATH,
57
+ authController,
58
+ collections: collectionsProp,
59
+ collectionPermissions,
60
+ views: viewsProp,
61
+ adminViews: adminViewsProp,
62
+ viewsOrder,
63
+ userConfigPersistence,
64
+ dataSourceDelegate,
65
+ injectCollections
66
+ } = props;
67
+
68
+ const collectionsRef = useRef<EntityCollection[] | undefined>();
69
+ const viewsRef = useRef<CMSView[] | undefined>();
70
+ const adminViewsRef = useRef<CMSView[] | undefined>();
71
+
63
72
  const [initialised, setInitialised] = useState<boolean>(false);
64
73
 
65
74
  const [topLevelNavigation, setTopLevelNavigation] = useState<TopNavigationResult | undefined>(undefined);
@@ -79,19 +88,18 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
79
88
  const buildUrlCollectionPath = useCallback((path: string): string => `${removeInitialAndTrailingSlashes(baseCollectionPath)}/${encodePath(path)}`,
80
89
  [baseCollectionPath]);
81
90
 
82
- const computeTopNavigation = useCallback((collections: EntityCollection[], views: CMSView[]): TopNavigationResult => {
83
- // return (collection.editable && resolvePermissions(collection, authController, paths).editCollection) ?? DEFAULT_PERMISSIONS.editCollection;
84
- const navigationEntries: TopNavigationEntry[] = [
91
+ const computeTopNavigation = useCallback((collections: EntityCollection[], views: CMSView[], adminViews: CMSView[], viewsOrder?: string[]): TopNavigationResult => {
92
+ let navigationEntries: TopNavigationEntry[] = [
85
93
  ...(collections ?? []).map(collection => (!collection.hideFromNavigation
86
- ? {
94
+ ? ({
87
95
  url: buildUrlCollectionPath(collection.id ?? collection.path),
88
96
  type: "collection",
89
97
  name: collection.name.trim(),
90
98
  path: collection.id ?? collection.path,
91
99
  collection,
92
100
  description: collection.description?.trim(),
93
- group: collection.group?.trim()
94
- }
101
+ group: getGroup(collection)
102
+ } satisfies TopNavigationEntry)
95
103
  : undefined))
96
104
  .filter(Boolean) as TopNavigationEntry[],
97
105
  ...(views ?? []).map(view =>
@@ -100,18 +108,74 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
100
108
  url: buildCMSUrlPath(Array.isArray(view.path) ? view.path[0] : view.path),
101
109
  name: view.name.trim(),
102
110
  type: "view",
111
+ path: view.path,
112
+ view,
113
+ description: view.description?.trim(),
114
+ group: getGroup(view)
115
+ } satisfies TopNavigationEntry)
116
+ : undefined)
117
+ .filter(Boolean) as TopNavigationEntry[],
118
+ ...(adminViews ?? []).map(view =>
119
+ !view.hideFromNavigation
120
+ ? ({
121
+ url: buildCMSUrlPath(Array.isArray(view.path) ? view.path[0] : view.path),
122
+ name: view.name.trim(),
123
+ type: "admin",
124
+ path: view.path,
103
125
  view,
104
126
  description: view.description?.trim(),
105
- group: view.group?.trim()
106
- })
127
+ group: "Admin"
128
+ } satisfies TopNavigationEntry)
107
129
  : undefined)
108
130
  .filter(Boolean) as TopNavigationEntry[]
109
131
  ];
110
132
 
133
+ // Sort by group, entries with group "Admin" will go last, and second to last will be the group "Views"
134
+ navigationEntries = navigationEntries.sort((a, b) => {
135
+ if (a.group !== "Views" && a.group !== "Admin" && (b.group === "Views" || b.group === "Admin")) {
136
+ return -1;
137
+ }
138
+ if (b.group !== "Views" && b.group !== "Admin" && (a.group === "Views" || a.group === "Admin")) {
139
+ return 1;
140
+ }
141
+ if (a.group === "Admin" && b.group !== "Admin") {
142
+ return 1;
143
+ }
144
+ if (a.group !== "Admin" && b.group === "Admin") {
145
+ return -1;
146
+ }
147
+ if (a.group === "Views" && b.group !== "Views") {
148
+ return -1;
149
+ }
150
+ if (a.group !== "Views" && b.group === "Views") {
151
+ return 1;
152
+ }
153
+ return 0;
154
+
155
+ });
156
+
157
+ if (viewsOrder) {
158
+ navigationEntries = navigationEntries.sort((a, b) => {
159
+ const aIndex = viewsOrder.indexOf(a.path);
160
+ const bIndex = viewsOrder.indexOf(b.path);
161
+ if (aIndex === -1 && bIndex === -1) {
162
+ return 0;
163
+ }
164
+ if (aIndex === -1) {
165
+ return 1;
166
+ }
167
+ if (bIndex === -1) {
168
+ return -1;
169
+ }
170
+ return aIndex - bIndex;
171
+ });
172
+ }
173
+
111
174
  const groups: string[] = Object.values(navigationEntries)
112
175
  .map(e => e.group)
113
176
  .filter(Boolean)
114
177
  .filter((value, index, array) => array.indexOf(value) === index) as string[];
178
+
115
179
  return {
116
180
  navigationEntries,
117
181
  groups
@@ -123,16 +187,39 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
123
187
  if (authController.initialLoading)
124
188
  return;
125
189
 
190
+ console.debug("Refreshing navigation");
191
+
126
192
  try {
127
- const [resolvedCollections = [], resolvedViews = []] = await Promise.all([
128
- resolveCollections(baseCollections, authController, dataSourceDelegate, injectCollections),
129
- resolveCMSViews(baseViews, authController, dataSourceDelegate)
193
+
194
+ const [resolvedCollections = [], resolvedViews, resolvedAdminViews = []] = await Promise.all([
195
+ resolveCollections(collectionsProp, collectionPermissions, authController, dataSourceDelegate, injectCollections),
196
+ resolveCMSViews(viewsProp, authController, dataSourceDelegate),
197
+ resolveCMSViews(adminViewsProp, authController, dataSourceDelegate)
130
198
  ]
131
199
  );
132
200
 
133
- setCollections(resolvedCollections);
134
- setViews(resolvedViews);
135
- setTopLevelNavigation(computeTopNavigation(resolvedCollections ?? [], resolvedViews));
201
+ let shouldUpdateTopLevelNav = false;
202
+ if (!areCollectionListsEqual(collectionsRef.current ?? [], resolvedCollections)) {
203
+ collectionsRef.current = resolvedCollections;
204
+ shouldUpdateTopLevelNav = true;
205
+ }
206
+ if (collectionsRef.current === undefined) {
207
+ collectionsRef.current = resolvedCollections;
208
+ shouldUpdateTopLevelNav = true;
209
+ }
210
+ if (!equal(viewsRef.current, resolvedViews)) {
211
+ viewsRef.current = resolvedViews;
212
+ shouldUpdateTopLevelNav = true;
213
+ }
214
+ if (!equal(adminViewsRef.current, resolvedAdminViews)) {
215
+ adminViewsRef.current = resolvedAdminViews;
216
+ shouldUpdateTopLevelNav = true;
217
+ }
218
+
219
+ const computedTopLevelNav = computeTopNavigation(resolvedCollections, resolvedViews, resolvedAdminViews, viewsOrder);
220
+ if (shouldUpdateTopLevelNav && !equal(topLevelNavigation, computedTopLevelNav)) {
221
+ setTopLevelNavigation(computedTopLevelNav);
222
+ }
136
223
  } catch (e) {
137
224
  console.error(e);
138
225
  setNavigationLoadingError(e as any);
@@ -140,26 +227,34 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
140
227
 
141
228
  setNavigationLoading(false);
142
229
  setInitialised(true);
143
- }, [baseCollections, authController.user, authController.initialLoading, baseViews, computeTopNavigation, injectCollections]);
230
+
231
+ }, [
232
+ collectionsProp,
233
+ collectionPermissions,
234
+ authController.user,
235
+ authController.initialLoading,
236
+ viewsProp,
237
+ adminViewsProp,
238
+ computeTopNavigation,
239
+ injectCollections
240
+ ]);
144
241
 
145
242
  useEffect(() => {
146
243
  refreshNavigation();
147
244
  }, [refreshNavigation]);
148
245
 
149
- const getCollection = useCallback(<EC extends EntityCollection>(
246
+ const getCollection = useCallback((
150
247
  idOrPath: string,
151
- entityId?: string,
152
248
  includeUserOverride = false
153
249
  ): EC | undefined => {
154
-
250
+ const collections = collectionsRef.current;
155
251
  if (!collections)
156
252
  return undefined;
157
253
 
158
254
  const baseCollection = getCollectionByPathOrId(removeInitialAndTrailingSlashes(idOrPath), collections);
159
255
 
160
256
  const userOverride = includeUserOverride ? userConfigPersistence?.getCollectionConfig(idOrPath) : undefined;
161
-
162
- const overriddenCollection = baseCollection ? mergeDeep(baseCollection, userOverride) : undefined;
257
+ const overriddenCollection = baseCollection ? mergeDeep(baseCollection, userOverride ?? {}) : undefined;
163
258
 
164
259
  let result: Partial<EntityCollection> | undefined = overriddenCollection;
165
260
 
@@ -179,16 +274,14 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
179
274
 
180
275
  return { ...overriddenCollection, ...result } as EC;
181
276
 
182
- }, [
183
- basePath,
184
- baseCollectionPath,
185
- collections,
186
- ]);
277
+ }, [userConfigPersistence]);
187
278
 
188
279
  const getCollectionFromPaths = useCallback(<EC extends EntityCollection>(pathSegments: string[]): EC | undefined => {
189
- let currentCollections = collections;
190
- if (!currentCollections)
191
- throw Error("Collections have not been initialised yet");
280
+
281
+ const collections = collectionsRef.current;
282
+ if (collections === undefined)
283
+ throw Error("getCollectionFromPaths: Collections have not been initialised yet");
284
+ let currentCollections: EntityCollection[] | undefined = [...(collections ?? [])];
192
285
 
193
286
  for (let i = 0; i < pathSegments.length; i++) {
194
287
  const pathSegment = pathSegments[i];
@@ -202,12 +295,14 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
202
295
 
203
296
  return undefined;
204
297
 
205
- }, [collections]);
298
+ }, []);
206
299
 
207
300
  const getCollectionFromIds = useCallback(<EC extends EntityCollection>(ids: string[]): EC | undefined => {
208
- let currentCollections = collections;
209
- if (!currentCollections)
210
- throw Error("Collections have not been initialised yet");
301
+
302
+ const collections = collectionsRef.current;
303
+ if (collections === undefined)
304
+ throw Error("getCollectionFromIds: Collections have not been initialised yet");
305
+ let currentCollections: EntityCollection[] | undefined = [...(collections ?? [])];
211
306
 
212
307
  for (let i = 0; i < ids.length; i++) {
213
308
  const id = ids[i];
@@ -221,7 +316,7 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
221
316
 
222
317
  return undefined;
223
318
 
224
- }, [collections]);
319
+ }, []);
225
320
 
226
321
  const isUrlCollectionPath = useCallback(
227
322
  (path: string): boolean => removeInitialAndTrailingSlashes(path + "/").startsWith(removeInitialAndTrailingSlashes(fullCollectionPath) + "/"),
@@ -243,25 +338,17 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
243
338
  []);
244
339
 
245
340
  const resolveAliasesFrom = useCallback((path: string): string => {
246
- if (!collections)
247
- throw Error("Collections have not been initialised yet");
341
+ const collections = collectionsRef.current ?? [];
248
342
  return resolveCollectionPathIds(path, collections);
249
- }, [collections]);
250
-
251
- const state = location.state as any;
252
- /**
253
- * The location can be overridden if `base_location` is set in the
254
- * state field of the current location. This can happen if you open
255
- * a side entity, like `products`, from a different one, like `users`
256
- */
257
- const baseLocation = state && state.base_location ? state.base_location : location;
343
+ }, []);
258
344
 
259
345
  const getAllParentReferencesForPath = useCallback((path: string): EntityReference[] => {
346
+ const collections = collectionsRef.current ?? [];
260
347
  return getParentReferencesFromPath({
261
348
  path,
262
349
  collections
263
350
  });
264
- }, [collections]);
351
+ }, []);
265
352
 
266
353
  const getParentCollectionIds = useCallback((path: string): string[] => {
267
354
 
@@ -276,35 +363,28 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
276
363
  }
277
364
 
278
365
  // for each odd path segment, get the collection
279
- const parentCollectionIds = result.map(r => getCollectionFromPaths(r)?.id).filter(Boolean) as string[];
280
-
281
- getCollectionFromPaths(oddPathSegments);
282
-
283
- // const allParentCollectionsForPath = getAllParentReferencesForPath(path);
284
- // console.log("allParentCollectionsForPath", allParentCollectionsForPath);
285
- // const parentCollectionIds = allParentCollectionsForPath.map(r => r.id);
286
- console.log("getParentCollectionIds", path, parentCollectionIds);
287
- return parentCollectionIds;
366
+ return result.map(r => getCollectionFromPaths(r)?.id).filter(Boolean) as string[];
288
367
  }, [getAllParentReferencesForPath])
289
368
 
290
369
  const convertIdsToPaths = useCallback((ids: string[]): string[] => {
291
- let currentCollections = collections;
292
- const paths: string[] = [];
293
- for (let i = 0; i < ids.length; i++) {
294
- const id = ids[i];
295
- const collection: EntityCollection | undefined = currentCollections!.find(c => c.id === id);
296
- if (!collection)
297
- throw Error(`Collection with id ${id} not found`);
298
- paths.push(collection.path);
299
- currentCollections = collection.subcollections;
300
- }
301
- return paths;
370
+ const collections = collectionsRef.current;
371
+ let currentCollections = collections;
372
+ const paths: string[] = [];
373
+ for (let i = 0; i < ids.length; i++) {
374
+ const id = ids[i];
375
+ const collection: EntityCollection | undefined = currentCollections!.find(c => c.id === id);
376
+ if (!collection)
377
+ throw Error(`Collection with id ${id} not found`);
378
+ paths.push(collection.path);
379
+ currentCollections = collection.subcollections;
302
380
  }
303
- , [getCollectionFromIds]);
381
+ return paths;
382
+ }, [getCollectionFromIds]);
304
383
 
305
384
  return {
306
- collections,
307
- views,
385
+ collections: collectionsRef.current,
386
+ views: viewsRef.current,
387
+ adminViews: adminViewsRef.current,
308
388
  loading: !initialised || navigationLoading,
309
389
  navigationLoadingError,
310
390
  homeUrl,
@@ -321,7 +401,6 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
321
401
  buildCMSUrlPath,
322
402
  resolveAliasesFrom,
323
403
  topLevelNavigation,
324
- baseLocation,
325
404
  refreshNavigation,
326
405
  getParentReferencesFromPath: getAllParentReferencesForPath,
327
406
  getParentCollectionIds,
@@ -346,8 +425,8 @@ function filterOutNotAllowedCollections(resolvedCollections: EntityCollection[],
346
425
  return resolvedCollections
347
426
  .filter((c) => {
348
427
  if (!c.permissions) return true;
349
- const resolvedPermissions = resolvePermissions(c, authController, [c.path], null,)
350
- return resolvedPermissions.read !== false;
428
+ const resolvedPermissions = resolvePermissions(c, authController, c.path, null)
429
+ return resolvedPermissions?.read !== false;
351
430
  })
352
431
  .map((c) => {
353
432
  if (!c.subcollections) return c;
@@ -359,9 +438,10 @@ function filterOutNotAllowedCollections(resolvedCollections: EntityCollection[],
359
438
  }
360
439
 
361
440
  async function resolveCollections(collections: undefined | EntityCollection[] | EntityCollectionsBuilder<any>,
441
+ collectionPermissions: PermissionsBuilder | undefined,
362
442
  authController: AuthController,
363
443
  dataSource: DataSourceDelegate,
364
- injectCollections?: (collections: EntityCollection[]) => EntityCollection[]) {
444
+ injectCollections?: (collections: EntityCollection[]) => EntityCollection[]): Promise<EntityCollection[]> {
365
445
  let resolvedCollections: EntityCollection[] = [];
366
446
  if (typeof collections === "function") {
367
447
  resolvedCollections = await collections({
@@ -373,12 +453,14 @@ async function resolveCollections(collections: undefined | EntityCollection[] |
373
453
  resolvedCollections = collections;
374
454
  }
375
455
 
376
- resolvedCollections = filterOutNotAllowedCollections(resolvedCollections, authController);
377
-
378
456
  if (injectCollections) {
379
457
  resolvedCollections = injectCollections(resolvedCollections ?? []);
380
458
  }
381
459
 
460
+ resolvedCollections = applyPermissionsFunctionIfEmpty(resolvedCollections, collectionPermissions);
461
+
462
+ resolvedCollections = filterOutNotAllowedCollections(resolvedCollections, authController);
463
+
382
464
  return resolvedCollections;
383
465
  }
384
466
 
@@ -395,3 +477,37 @@ async function resolveCMSViews(baseViews: CMSView[] | CMSViewsBuilder | undefine
395
477
  }
396
478
  return resolvedViews;
397
479
  }
480
+
481
+ function getGroup(collectionOrView: EntityCollection<any, any> | CMSView) {
482
+ const trimmed = collectionOrView.group?.trim();
483
+ if (!trimmed || trimmed === "") {
484
+ return "Views";
485
+ }
486
+ return trimmed ?? "Views";
487
+ }
488
+
489
+ function areCollectionListsEqual(a: EntityCollection[], b: EntityCollection[]) {
490
+ if (a.length !== b.length) {
491
+ return false;
492
+ }
493
+ const aCopy = [...a];
494
+ const bCopy = [...b];
495
+ const aSorted = aCopy.sort((x, y) => x.id.localeCompare(y.id));
496
+ const bSorted = bCopy.sort((x, y) => x.id.localeCompare(y.id));
497
+ return aSorted.every((value, index) => areCollectionsEqual(value, bSorted[index]));
498
+ }
499
+
500
+ function areCollectionsEqual(a: EntityCollection, b: EntityCollection) {
501
+ const {
502
+ subcollections: subcollectionsA,
503
+ ...restA
504
+ } = a;
505
+ const {
506
+ subcollections: subcollectionsB,
507
+ ...restB
508
+ } = b;
509
+ if (!areCollectionListsEqual(subcollectionsA ?? [], subcollectionsB ?? [])) {
510
+ return false;
511
+ }
512
+ return equal(restA, restB);
513
+ }
@@ -1,31 +1,52 @@
1
- import { useEffect, useRef } from "react";
2
- import { AuthController } from "../types";
1
+ import { useEffect, useRef, useState } from "react";
2
+ import { AuthController, DataSourceDelegate, FireCMSPlugin } from "../types";
3
3
 
4
4
  export const DEFAULT_SERVER_DEV = "https://api-kdoe6pj3qq-ey.a.run.app";
5
5
  export const DEFAULT_SERVER = "https://api-drplyi3b6q-ey.a.run.app";
6
6
 
7
- async function makeRequest(authController: AuthController) {
8
- const firebaseToken = await authController.getAuthToken();
7
+ export type AccessResponse = {
8
+ blocked?: boolean;
9
+ message?: string;
10
+ }
11
+
12
+ async function makeRequest(authController: AuthController, dataSourceKey: string, pluginKeys: string[] | undefined) {
13
+ let idToken: string | null;
14
+ try {
15
+ idToken = await authController.getAuthToken();
16
+ } catch (e) {
17
+ idToken = null;
18
+ }
9
19
  return fetch(DEFAULT_SERVER + "/access_log",
10
20
  {
11
21
  // mode: "no-cors",
12
22
  method: "POST",
13
23
  headers: {
14
24
  "Content-Type": "application/json",
15
- Authorization: `Basic ${firebaseToken}`,
25
+ Authorization: `Basic ${idToken}`
16
26
  },
17
- body: JSON.stringify({})
27
+ body: JSON.stringify({
28
+ email: authController.user?.email ?? null,
29
+ datasource: dataSourceKey,
30
+ plugins: pluginKeys
31
+ })
18
32
  })
19
33
  .then(async (res) => {
34
+ return res.json();
20
35
  });
21
36
  }
22
37
 
23
- export function useProjectLog(authController: AuthController) {
38
+ export function useProjectLog(authController: AuthController,
39
+ dataSourceDelegate: DataSourceDelegate,
40
+ plugins?: FireCMSPlugin<any, any, any>[]): AccessResponse | null {
41
+ const [accessResponse, setAccessResponse] = useState<AccessResponse | null>(null);
24
42
  const accessedUserRef = useRef<string | null>(null);
43
+ const dataSourceKey = dataSourceDelegate.key;
44
+ const pluginKeys = plugins?.map(plugin => plugin.key);
25
45
  useEffect(() => {
26
46
  if (authController.user && authController.user.uid !== accessedUserRef.current && !authController.initialLoading) {
27
- makeRequest(authController);
47
+ makeRequest(authController, dataSourceKey, pluginKeys).then(setAccessResponse);
28
48
  accessedUserRef.current = authController.user.uid;
29
49
  }
30
- }, [authController]);
50
+ }, [authController, dataSourceKey, pluginKeys]);
51
+ return accessResponse;
31
52
  }
@@ -1,5 +1,5 @@
1
1
  import { useSideDialogsController } from "./useSideDialogsController";
2
- import { ReferenceSelectionTable, ReferenceSelectionInnerProps } from "../components";
2
+ import { ReferenceSelectionInnerProps, ReferenceSelectionTable } from "../components";
3
3
  import { useCallback } from "react";
4
4
  import { useNavigationController } from "./useNavigationController";
5
5
 
@@ -27,7 +27,7 @@ export function useReferenceDialog<M extends Record<string, any>>(referenceDialo
27
27
  if (!usedCollection)
28
28
  usedCollection = navigation.getCollection(referenceDialogProps.path);
29
29
  if (!usedCollection)
30
- throw Error("Not able to resolve the collection in useReferenceDialog");
30
+ throw Error("Not able to resolve the collection in useReferenceDialog. Make sure a collection is registered in path " + referenceDialogProps.path);
31
31
  sideDialogsController.open({
32
32
  key: `reference_${referenceDialogProps.path}`,
33
33
  component:
@@ -80,7 +80,7 @@ export function resolveNavigationFrom<M extends Record<string, any>, UserType ex
80
80
  if (entry.type === "collection") {
81
81
  return Promise.resolve(entry);
82
82
  } else if (entry.type === "entity") {
83
- const collection = navigation.getCollection(entry.path, entry.entityId);
83
+ const collection = navigation.getCollection(entry.path);
84
84
  if (!collection) {
85
85
  throw Error(`No collection defined in the navigation for the entity with path ${entry.path}`);
86
86
  }
@@ -1,4 +1,4 @@
1
- import { StorageSource } from "../types";
1
+ import { EntityCollection, StorageSource } from "../types";
2
2
  import { StorageSourceContext } from "../contexts/StorageSourceContext";
3
3
  import { useContext } from "react";
4
4
 
@@ -6,4 +6,9 @@ import { useContext } from "react";
6
6
  * Use this hook to get the storage source being used
7
7
  * @group Hooks and utilities
8
8
  */
9
- export const useStorageSource = (): StorageSource => useContext(StorageSourceContext);
9
+ export const useStorageSource = (collection?: EntityCollection): StorageSource => {
10
+ const defaultStorageSource = useContext(StorageSourceContext);
11
+ if (collection?.overrides?.storageSource)
12
+ return collection.overrides.storageSource;
13
+ return defaultStorageSource;
14
+ };