@firecms/core 3.0.0-canary.6 → 3.0.0-canary.61

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 (242) 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/EntityCollectionTableProps.d.ts +5 -3
  6. package/dist/components/EntityCollectionTable/PropertyTableCell.d.ts +3 -2
  7. package/dist/components/EntityCollectionTable/column_utils.d.ts +1 -2
  8. package/dist/components/EntityCollectionTable/fields/TableReferenceField.d.ts +2 -0
  9. package/dist/components/EntityCollectionTable/internal/CollectionTableToolbar.d.ts +1 -4
  10. package/dist/components/EntityCollectionTable/internal/EntityTableCell.d.ts +2 -2
  11. package/dist/components/EntityCollectionTable/internal/popup_field/PopupFormField.d.ts +1 -1
  12. package/dist/components/EntityCollectionView/EntityCollectionView.d.ts +12 -3
  13. package/dist/components/EntityCollectionView/EntityCollectionViewStartActions.d.ts +11 -0
  14. package/dist/components/EntityCollectionView/useSelectionController.d.ts +2 -0
  15. package/dist/components/EntityPreview.d.ts +26 -7
  16. package/dist/components/EntityView.d.ts +11 -0
  17. package/dist/components/FieldCaption.d.ts +5 -0
  18. package/dist/components/FireCMSAppBar.d.ts +4 -2
  19. package/dist/components/HomePage/NavigationCard.d.ts +8 -0
  20. package/dist/components/HomePage/{NavigationCollectionCard.d.ts → NavigationCardBinding.d.ts} +2 -2
  21. package/dist/components/HomePage/SmallNavigationCard.d.ts +6 -0
  22. package/dist/components/HomePage/index.d.ts +3 -1
  23. package/dist/components/ReferenceWidget.d.ts +3 -1
  24. package/dist/components/SelectableTable/SelectableTable.d.ts +1 -1
  25. package/dist/components/SelectableTable/filters/ReferenceFilterField.d.ts +2 -1
  26. package/dist/components/VirtualTable/VirtualTableProps.d.ts +6 -7
  27. package/dist/components/VirtualTable/types.d.ts +3 -3
  28. package/dist/components/{EntityCollectionTable/internal → common}/default_entity_actions.d.ts +1 -1
  29. package/dist/components/common/index.d.ts +1 -0
  30. package/dist/components/common/table_height.d.ts +5 -0
  31. package/dist/components/common/types.d.ts +4 -6
  32. package/dist/components/common/useDataSourceEntityCollectionTableController.d.ts +3 -0
  33. package/dist/components/index.d.ts +5 -2
  34. package/dist/contexts/AuthControllerContext.d.ts +1 -1
  35. package/dist/core/Drawer.d.ts +5 -12
  36. package/dist/core/DrawerNavigationItem.d.ts +9 -0
  37. package/dist/core/{EntityView.d.ts → EntityEditView.d.ts} +2 -2
  38. package/dist/core/NavigationRoutes.d.ts +1 -1
  39. package/dist/core/Scaffold.d.ts +8 -12
  40. package/dist/core/index.d.ts +3 -4
  41. package/dist/form/EntityForm.d.ts +1 -1
  42. package/dist/form/components/ErrorFocus.d.ts +1 -1
  43. package/dist/form/components/StorageItemPreview.d.ts +3 -2
  44. package/dist/form/components/StorageUploadProgress.d.ts +1 -1
  45. package/dist/form/field_bindings/KeyValueFieldBinding.d.ts +1 -1
  46. package/dist/form/field_bindings/MapFieldBinding.d.ts +1 -1
  47. package/dist/form/field_bindings/StorageUploadFieldBinding.d.ts +4 -3
  48. package/dist/form/field_bindings/TextFieldBinding.d.ts +2 -2
  49. package/dist/form/validation.d.ts +1 -1
  50. package/dist/hooks/data/delete.d.ts +2 -2
  51. package/dist/hooks/data/save.d.ts +2 -3
  52. package/dist/hooks/data/useDataSource.d.ts +2 -2
  53. package/dist/hooks/data/useEntityFetch.d.ts +3 -3
  54. package/dist/hooks/index.d.ts +2 -0
  55. package/dist/hooks/useBuildNavigationController.d.ts +6 -4
  56. package/dist/hooks/useProjectLog.d.ts +6 -2
  57. package/dist/hooks/useStorageSource.d.ts +2 -2
  58. package/dist/hooks/useValidateAuthenticator.d.ts +21 -0
  59. package/dist/index.es.js +10498 -9944
  60. package/dist/index.es.js.map +1 -1
  61. package/dist/index.umd.js +5 -5
  62. package/dist/index.umd.js.map +1 -1
  63. package/dist/internal/useBuildDataSource.d.ts +1 -16
  64. package/dist/preview/PropertyPreview.d.ts +1 -1
  65. package/dist/preview/PropertyPreviewProps.d.ts +1 -4
  66. package/dist/preview/components/BooleanPreview.d.ts +5 -1
  67. package/dist/preview/components/EnumValuesChip.d.ts +1 -1
  68. package/dist/preview/components/ReferencePreview.d.ts +3 -8
  69. package/dist/types/analytics.d.ts +1 -1
  70. package/dist/types/auth.d.ts +37 -1
  71. package/dist/types/collections.d.ts +30 -6
  72. package/dist/types/datasource.d.ts +21 -14
  73. package/dist/types/entities.d.ts +5 -1
  74. package/dist/types/entity_actions.d.ts +14 -0
  75. package/dist/types/entity_callbacks.d.ts +2 -2
  76. package/dist/types/entity_overrides.d.ts +6 -0
  77. package/dist/types/index.d.ts +2 -1
  78. package/dist/types/navigation.d.ts +15 -14
  79. package/dist/types/permissions.d.ts +5 -1
  80. package/dist/types/plugins.d.ts +20 -20
  81. package/dist/types/properties.d.ts +12 -4
  82. package/dist/types/property_config.d.ts +2 -2
  83. package/dist/types/roles.d.ts +31 -0
  84. package/dist/types/storage.d.ts +11 -3
  85. package/dist/types/user.d.ts +5 -0
  86. package/dist/util/collections.d.ts +9 -1
  87. package/dist/util/entities.d.ts +1 -1
  88. package/dist/util/icon_synonyms.d.ts +1 -97
  89. package/dist/util/icons.d.ts +8 -2
  90. package/dist/util/navigation_utils.d.ts +2 -2
  91. package/dist/util/objects.d.ts +1 -1
  92. package/dist/util/permissions.d.ts +4 -4
  93. package/dist/util/references.d.ts +4 -2
  94. package/dist/util/resolutions.d.ts +14 -14
  95. package/dist/util/useTraceUpdate.d.ts +1 -0
  96. package/package.json +139 -119
  97. package/src/components/ClearFilterSortButton.tsx +41 -0
  98. package/src/components/DeleteEntityDialog.tsx +4 -4
  99. package/src/components/EntityCollectionTable/EntityCollectionRowActions.tsx +4 -4
  100. package/src/components/EntityCollectionTable/EntityCollectionTable.tsx +276 -279
  101. package/src/components/EntityCollectionTable/EntityCollectionTableProps.tsx +9 -5
  102. package/src/components/EntityCollectionTable/PropertyTableCell.tsx +48 -45
  103. package/src/components/EntityCollectionTable/column_utils.tsx +3 -3
  104. package/src/components/EntityCollectionTable/fields/TableReferenceField.tsx +18 -17
  105. package/src/components/EntityCollectionTable/fields/TableStorageUpload.tsx +5 -5
  106. package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +29 -34
  107. package/src/components/EntityCollectionTable/internal/EntityTableCell.tsx +16 -12
  108. package/src/components/EntityCollectionTable/internal/popup_field/PopupFormField.tsx +2 -4
  109. package/src/components/EntityCollectionView/EntityCollectionView.tsx +73 -72
  110. package/src/components/EntityCollectionView/EntityCollectionViewActions.tsx +5 -6
  111. package/src/components/EntityCollectionView/EntityCollectionViewStartActions.tsx +68 -0
  112. package/src/components/EntityCollectionView/useSelectionController.tsx +30 -0
  113. package/src/components/EntityPreview.tsx +209 -70
  114. package/src/components/EntityView.tsx +84 -0
  115. package/src/components/FieldCaption.tsx +14 -0
  116. package/src/components/FireCMSAppBar.tsx +40 -15
  117. package/src/components/HomePage/DefaultHomePage.tsx +15 -11
  118. package/src/components/HomePage/NavigationCard.tsx +69 -0
  119. package/src/components/HomePage/NavigationCardBinding.tsx +116 -0
  120. package/src/components/HomePage/SmallNavigationCard.tsx +45 -0
  121. package/src/components/HomePage/index.tsx +3 -1
  122. package/src/components/PropertyIdCopyTooltipContent.tsx +2 -3
  123. package/src/components/ReferenceTable/ReferenceSelectionTable.tsx +4 -4
  124. package/src/components/ReferenceWidget.tsx +22 -12
  125. package/src/components/SearchIconsView.tsx +5 -5
  126. package/src/components/SelectableTable/SelectableTable.tsx +5 -3
  127. package/src/components/SelectableTable/filters/BooleanFilterField.tsx +2 -3
  128. package/src/components/SelectableTable/filters/DateTimeFilterField.tsx +23 -8
  129. package/src/components/SelectableTable/filters/ReferenceFilterField.tsx +38 -24
  130. package/src/components/SelectableTable/filters/StringNumberFilterField.tsx +35 -15
  131. package/src/components/VirtualTable/VirtualTable.tsx +38 -29
  132. package/src/components/VirtualTable/VirtualTableHeader.tsx +4 -4
  133. package/src/components/VirtualTable/VirtualTableHeaderRow.tsx +2 -2
  134. package/src/components/VirtualTable/VirtualTableProps.tsx +7 -7
  135. package/src/components/VirtualTable/VirtualTableRow.tsx +4 -5
  136. package/src/components/VirtualTable/fields/VirtualTableDateField.tsx +1 -1
  137. package/src/components/VirtualTable/types.tsx +2 -3
  138. package/src/components/{EntityCollectionTable/internal → common}/default_entity_actions.tsx +11 -7
  139. package/src/components/common/index.ts +1 -0
  140. package/src/components/{VirtualTable/common.tsx → common/table_height.tsx} +5 -2
  141. package/src/components/common/types.tsx +4 -6
  142. package/src/components/common/useColumnsIds.tsx +10 -2
  143. package/src/components/common/useDataSourceEntityCollectionTableController.tsx +12 -1
  144. package/src/components/common/useTableSearchHelper.ts +39 -9
  145. package/src/components/index.tsx +5 -2
  146. package/src/contexts/AuthControllerContext.tsx +1 -1
  147. package/src/core/Drawer.tsx +78 -103
  148. package/src/core/DrawerNavigationItem.tsx +62 -0
  149. package/src/core/{EntityView.tsx → EntityEditView.tsx} +27 -45
  150. package/src/core/EntitySidePanel.tsx +3 -3
  151. package/src/core/FireCMS.tsx +54 -43
  152. package/src/core/NavigationRoutes.tsx +11 -4
  153. package/src/core/Scaffold.tsx +80 -66
  154. package/src/core/field_configs.tsx +2 -3
  155. package/src/core/index.tsx +3 -4
  156. package/src/form/EntityForm.tsx +42 -27
  157. package/src/form/PropertyFieldBinding.tsx +0 -2
  158. package/src/form/components/StorageItemPreview.tsx +7 -5
  159. package/src/form/components/StorageUploadProgress.tsx +9 -8
  160. package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +10 -12
  161. package/src/form/field_bindings/BlockFieldBinding.tsx +2 -2
  162. package/src/form/field_bindings/DateTimeFieldBinding.tsx +1 -1
  163. package/src/form/field_bindings/KeyValueFieldBinding.tsx +19 -19
  164. package/src/form/field_bindings/MapFieldBinding.tsx +15 -15
  165. package/src/form/field_bindings/MarkdownFieldBinding.tsx +2 -2
  166. package/src/form/field_bindings/ReadOnlyFieldBinding.tsx +3 -3
  167. package/src/form/field_bindings/ReferenceFieldBinding.tsx +16 -13
  168. package/src/form/field_bindings/SelectFieldBinding.tsx +3 -3
  169. package/src/form/field_bindings/StorageUploadFieldBinding.tsx +18 -9
  170. package/src/form/field_bindings/TextFieldBinding.tsx +7 -5
  171. package/src/form/validation.ts +3 -4
  172. package/src/hooks/data/delete.ts +3 -3
  173. package/src/hooks/data/save.ts +4 -2
  174. package/src/hooks/data/useCollectionFetch.tsx +1 -1
  175. package/src/hooks/data/useDataSource.tsx +8 -3
  176. package/src/hooks/data/useEntityFetch.tsx +4 -4
  177. package/src/hooks/index.tsx +3 -0
  178. package/src/hooks/useBuildLocalConfigurationPersistence.tsx +8 -10
  179. package/src/hooks/useBuildModeController.tsx +11 -5
  180. package/src/hooks/useBuildNavigationController.tsx +200 -83
  181. package/src/hooks/useProjectLog.tsx +17 -7
  182. package/src/hooks/useReferenceDialog.tsx +2 -2
  183. package/src/hooks/useResolvedNavigationFrom.tsx +1 -1
  184. package/src/hooks/useStorageSource.tsx +7 -2
  185. package/src/hooks/useValidateAuthenticator.tsx +115 -0
  186. package/src/internal/useBuildDataSource.ts +54 -47
  187. package/src/internal/useBuildSideEntityController.tsx +88 -21
  188. package/src/preview/PropertyPreview.tsx +5 -15
  189. package/src/preview/PropertyPreviewProps.tsx +1 -11
  190. package/src/preview/components/BooleanPreview.tsx +19 -4
  191. package/src/preview/components/EnumValuesChip.tsx +2 -2
  192. package/src/preview/components/ReferencePreview.tsx +72 -165
  193. package/src/preview/property_previews/ArrayOfMapsPreview.tsx +0 -1
  194. package/src/preview/property_previews/ArrayOfReferencesPreview.tsx +2 -1
  195. package/src/preview/property_previews/ArrayOfStorageComponentsPreview.tsx +0 -1
  196. package/src/preview/property_previews/ArrayOfStringsPreview.tsx +0 -1
  197. package/src/preview/property_previews/ArrayOneOfPreview.tsx +2 -3
  198. package/src/preview/property_previews/ArrayPropertyPreview.tsx +2 -3
  199. package/src/preview/property_previews/MapPropertyPreview.tsx +5 -5
  200. package/src/preview/property_previews/StringPropertyPreview.tsx +8 -7
  201. package/src/types/analytics.ts +1 -0
  202. package/src/types/auth.tsx +50 -1
  203. package/src/types/collections.ts +37 -6
  204. package/src/types/datasource.ts +24 -17
  205. package/src/types/entities.ts +9 -1
  206. package/src/types/entity_actions.tsx +17 -0
  207. package/src/types/entity_callbacks.ts +2 -2
  208. package/src/types/entity_overrides.tsx +7 -0
  209. package/src/types/firecms.tsx +0 -1
  210. package/src/types/index.ts +2 -1
  211. package/src/types/navigation.ts +17 -17
  212. package/src/types/permissions.ts +6 -1
  213. package/src/types/plugins.tsx +26 -28
  214. package/src/types/properties.ts +18 -6
  215. package/src/types/property_config.tsx +2 -2
  216. package/src/types/roles.ts +41 -0
  217. package/src/types/side_entity_controller.tsx +1 -0
  218. package/src/types/storage.ts +12 -3
  219. package/src/types/user.ts +7 -0
  220. package/src/util/collections.ts +22 -0
  221. package/src/util/entities.ts +1 -1
  222. package/src/util/enums.ts +1 -1
  223. package/src/util/icon_list.ts +2 -2
  224. package/src/util/icon_synonyms.ts +3 -99
  225. package/src/util/icons.tsx +11 -3
  226. package/src/util/navigation_utils.ts +6 -6
  227. package/src/util/objects.ts +8 -21
  228. package/src/util/permissions.ts +11 -8
  229. package/src/util/references.ts +36 -5
  230. package/src/util/resolutions.ts +32 -31
  231. package/src/util/strings.ts +2 -2
  232. package/src/util/useTraceUpdate.tsx +2 -1
  233. package/dist/components/VirtualTable/common.d.ts +0 -2
  234. package/dist/core/SideEntityView.d.ts +0 -7
  235. package/dist/internal/useBuildCustomizationController.d.ts +0 -2
  236. package/dist/internal/useLocaleConfig.d.ts +0 -1
  237. package/dist/types/appcheck.d.ts +0 -26
  238. package/src/components/HomePage/NavigationCollectionCard.tsx +0 -146
  239. package/src/core/SideEntityView.tsx +0 -38
  240. package/src/internal/useBuildCustomizationController.tsx +0 -5
  241. package/src/internal/useLocaleConfig.tsx +0 -18
  242. package/src/types/appcheck.ts +0 -29
@@ -1,84 +1,223 @@
1
- import React, { useMemo } from "react";
2
- import { PropertyPreview } from "../preview";
3
- import { Entity, EntityCollection, ResolvedEntityCollection, ResolvedProperties } from "../types";
4
- import { resolveCollection } from "../util";
5
- import { cn, defaultBorderMixin, IconButton, OpenInNewIcon } from "@firecms/ui";
6
- import { CustomizationController } from "../types/customization_controller";
7
- import { useCustomizationController } from "../hooks/useCustomizationController";
1
+ import * as React from "react";
2
+ import { useMemo } from "react";
3
+
4
+ import { Entity, EntityCollection, ResolvedProperty } from "../types";
5
+
6
+ import {
7
+ getEntityImagePreviewPropertyKey,
8
+ getEntityPreviewKeys,
9
+ getEntityTitlePropertyKey,
10
+ getValueInPath,
11
+ resolveCollection
12
+ } from "../util";
13
+ import { cls, defaultBorderMixin, IconButton, KeyboardTabIcon, Skeleton, Tooltip, Typography } from "@firecms/ui";
14
+ import { PreviewSize, PropertyPreview, SkeletonPropertyComponent } from "../preview";
15
+ import { useCustomizationController, useNavigationController, useSideEntityController } from "../hooks";
16
+ import { useAnalyticsController } from "../hooks/useAnalyticsController";
17
+
18
+ export type EntityPreviewProps = {
19
+ size: PreviewSize,
20
+ actions?: React.ReactNode,
21
+ collection?: EntityCollection,
22
+ hover?: boolean;
23
+ previewProperties?: string[],
24
+ disabled: undefined | boolean,
25
+ entity: Entity<any>,
26
+ includeId?: boolean,
27
+ includeEntityLink?: boolean,
28
+ onClick?: (e: React.SyntheticEvent) => void;
29
+ };
8
30
 
9
31
  /**
10
- * @group Components
32
+ * This view is used to display a preview of an entity.
33
+ * It is used by default in reference fields and whenever a reference is displayed.
11
34
  */
12
- export interface EntityPreviewProps<M extends Record<string, any>> {
13
- entity: Entity<M>;
14
- collection: EntityCollection<M>;
15
- path: string;
16
- className?: string;
17
- }
35
+ export function EntityPreview({
36
+ actions,
37
+ disabled,
38
+ hover,
39
+ collection: collectionProp,
40
+ previewProperties,
41
+ onClick,
42
+ size,
43
+ includeId = true,
44
+ includeEntityLink = true,
45
+ entity
46
+ }: EntityPreviewProps) {
18
47
 
19
- export function EntityPreview<M extends Record<string, any>>(
20
- {
21
- entity,
22
- collection,
23
- path,
24
- className
25
- }: EntityPreviewProps<M>) {
48
+ const analyticsController = useAnalyticsController();
49
+ const sideEntityController = useSideEntityController();
50
+ const customizationController = useCustomizationController();
51
+
52
+ const navigationController = useNavigationController();
53
+
54
+ const collection = collectionProp ?? navigationController.getCollection(entity.path);
26
55
 
27
- const customizationController: CustomizationController = useCustomizationController();
28
- const resolvedCollection: ResolvedEntityCollection<M> = useMemo(() => resolveCollection<M>({
56
+ if (!collection) {
57
+ throw Error(`Couldn't find the corresponding collection view for the path: ${entity.path}`);
58
+ }
59
+
60
+ const resolvedCollection = React.useMemo(() => resolveCollection({
29
61
  collection,
30
- path,
31
- entityId: entity.id,
62
+ path: entity.path,
32
63
  values: entity.values,
33
64
  fields: customizationController.propertyConfigs
34
- }), [collection, path, entity]);
65
+ }), [collection]);
35
66
 
36
- const properties: ResolvedProperties = resolvedCollection.properties;
67
+ const listProperties = useMemo(() => getEntityPreviewKeys(resolvedCollection, customizationController.propertyConfigs, previewProperties, size === "small" || size === "medium" ? 3 : 1),
68
+ [previewProperties, resolvedCollection, size]);
37
69
 
38
- return (
39
- <div className={"w-full " + className}>
40
- <div className={"w-full mb-4"}>
41
- <div className={cn(defaultBorderMixin, "flex justify-between py-2 border-b last:border-b-0")}>
42
- <div className="flex items-center w-1/4">
43
- <span className="pl-2 text-sm text-gray-600">Id</span>
44
- </div>
45
- <div
46
- className="flex-grow p-2 ml-2 w-3/4 text-gray-900 dark:text-white min-h-[56px] flex items-center">
47
- <span className="flex-grow mr-2">{entity.id}</span>
48
- {customizationController?.entityLinkBuilder &&
49
- <a href={customizationController.entityLinkBuilder({ entity })}
50
- rel="noopener noreferrer"
51
- target="_blank">
52
- <IconButton>
53
- <OpenInNewIcon
54
- size={"small"}/>
55
- </IconButton>
56
- </a>}
70
+ const titleProperty = getEntityTitlePropertyKey(resolvedCollection, customizationController.propertyConfigs);
71
+ const imagePropertyKey = getEntityImagePreviewPropertyKey(resolvedCollection);
72
+ const imageProperty = imagePropertyKey ? resolvedCollection.properties[imagePropertyKey] : undefined;
73
+
74
+ const restProperties = listProperties.filter(p => p !== titleProperty && p !== imagePropertyKey);
75
+
76
+ return <EntityPreviewContainer onClick={disabled ? undefined : onClick}
77
+ hover={disabled ? undefined : hover}
78
+ size={size}>
79
+ {imageProperty && (
80
+ <div className={cls("w-10 h-10 mr-2 shrink-0 grow-0", size === "tiny" ? "my-0.5" : "m-2 self-start")}>
81
+ <PropertyPreview property={imageProperty}
82
+ propertyKey={imagePropertyKey as string}
83
+ size={"tiny"}
84
+ value={getValueInPath(entity.values, imagePropertyKey as string)}/>
85
+ </div>
86
+ )}
87
+
88
+ <div className={"flex flex-col flex-grow w-full m-1"}>
89
+
90
+ {size !== "tiny" && includeId && (
91
+ entity
92
+ ? <div className={`${
93
+ size !== "medium"
94
+ ? "block whitespace-nowrap overflow-hidden truncate"
95
+ : ""
96
+ }`}>
97
+ <Typography variant={"caption"}
98
+ color={"disabled"}
99
+ className={"font-mono"}>
100
+ {entity.id}
101
+ </Typography>
57
102
  </div>
103
+ : <Skeleton/>)}
104
+
105
+ {titleProperty && (
106
+ <div className={"my-0.5 text-sm font-medium"}>
107
+ {
108
+ entity
109
+ ? <PropertyPreview
110
+ propertyKey={titleProperty as string}
111
+ value={getValueInPath(entity.values, titleProperty)}
112
+ property={resolvedCollection.properties[titleProperty as string] as ResolvedProperty}
113
+ size={"medium"}/>
114
+ : <SkeletonPropertyComponent
115
+ property={resolvedCollection.properties[titleProperty as string] as ResolvedProperty}
116
+ size={"medium"}/>
117
+ }
58
118
  </div>
59
- {Object.entries(properties)
60
- .map(([key, property]) => {
61
- const value = (entity.values)[key];
62
- return (
63
- <div
64
- key={`reference_previews_${key}`}
65
- className={cn(defaultBorderMixin, "flex justify-between py-2 border-b last:border-b-0")}>
66
- <div className="flex items-center w-1/4">
67
- <span className="pl-2 text-sm text-gray-600">{property.name}</span>
68
- </div>
69
- <div
70
- className="flex-grow p-2 ml-2 w-3/4 text-gray-900 dark:text-white min-h-[56px] flex items-center">
71
- <PropertyPreview
72
- propertyKey={key}
73
- value={value}
74
- // entity={entity}
75
- property={property}
76
- size={"medium"}/>
77
- </div>
78
- </div>
79
- )
80
- })}
81
- </div>
119
+ )}
120
+
121
+ {restProperties && restProperties.map((key) => {
122
+ const childProperty = resolvedCollection.properties[key as string];
123
+ if (!childProperty) return null;
124
+
125
+ return (
126
+ <div key={"ref_prev_" + key}
127
+ className={restProperties.length > 1 ? "my-0.5" : "my-0"}>
128
+ {
129
+ entity
130
+ ? <PropertyPreview
131
+ propertyKey={key as string}
132
+ value={getValueInPath(entity.values, key)}
133
+ property={childProperty as ResolvedProperty}
134
+ size={"tiny"}/>
135
+ : <SkeletonPropertyComponent
136
+ property={childProperty as ResolvedProperty}
137
+ size={"tiny"}/>
138
+ }
139
+ </div>
140
+ );
141
+ })}
142
+
82
143
  </div>
83
- );
144
+
145
+ {entity && includeEntityLink &&
146
+ <Tooltip title={`See details for ${entity.id}`}
147
+ className={size !== "tiny" ? "self-start" : ""}>
148
+ <IconButton
149
+ color={"inherit"}
150
+ size={"small"}
151
+ onClick={(e) => {
152
+ e.stopPropagation();
153
+ analyticsController.onAnalyticsEvent?.("entity_click_from_reference", {
154
+ path: entity.path,
155
+ entityId: entity.id
156
+ });
157
+ sideEntityController.open({
158
+ entityId: entity.id,
159
+ path: entity.path,
160
+ collection,
161
+ updateUrl: true
162
+ });
163
+ }}>
164
+ <KeyboardTabIcon size={"small"}/>
165
+ </IconButton>
166
+ </Tooltip>}
167
+
168
+ {actions}
169
+
170
+ </EntityPreviewContainer>;
84
171
  }
172
+
173
+ export type EntityPreviewContainerProps = {
174
+ children: React.ReactNode;
175
+ hover?: boolean;
176
+ fullwidth?: boolean;
177
+ size: PreviewSize;
178
+ className?: string;
179
+ style?: React.CSSProperties;
180
+ onClick?: (e: React.SyntheticEvent) => void;
181
+ };
182
+
183
+ const EntityPreviewContainerInner = React.forwardRef<HTMLDivElement, EntityPreviewContainerProps>(({
184
+ children,
185
+ hover,
186
+ onClick,
187
+ size,
188
+ style,
189
+ className,
190
+ fullwidth = true,
191
+ ...props
192
+ }, ref) => {
193
+ return <div
194
+ ref={ref}
195
+ style={{
196
+ ...style,
197
+ // @ts-ignore
198
+ tabindex: 0
199
+ }}
200
+ className={cls(
201
+ "bg-white dark:bg-gray-900",
202
+ fullwidth ? "w-full" : "",
203
+ "items-center",
204
+ hover ? "hover:bg-slate-50 dark:hover:bg-gray-800 group-hover:bg-slate-50 dark:group-hover:bg-gray-800" : "",
205
+ size === "tiny" ? "p-1" : "p-2",
206
+ "flex border rounded-lg",
207
+ onClick ? "cursor-pointer" : "",
208
+ defaultBorderMixin,
209
+ className)}
210
+ onClick={(event) => {
211
+ if (onClick) {
212
+ event.preventDefault();
213
+ onClick(event);
214
+ }
215
+ }}
216
+ {...props}>
217
+ {children}
218
+ </div>;
219
+ });
220
+
221
+ EntityPreviewContainerInner.displayName = "EntityPreviewContainer";
222
+
223
+ export const EntityPreviewContainer = React.memo(EntityPreviewContainerInner) as React.FC<EntityPreviewContainerProps>;
@@ -0,0 +1,84 @@
1
+ import React, { useMemo } from "react";
2
+ import { PropertyPreview } from "../preview";
3
+ import { Entity, EntityCollection, ResolvedEntityCollection, ResolvedProperties } from "../types";
4
+ import { resolveCollection } from "../util";
5
+ import { cls, defaultBorderMixin, IconButton, OpenInNewIcon } from "@firecms/ui";
6
+ import { CustomizationController } from "../types/customization_controller";
7
+ import { useCustomizationController } from "../hooks/useCustomizationController";
8
+
9
+ /**
10
+ * @group Components
11
+ */
12
+ export interface EntityViewProps<M extends Record<string, any>> {
13
+ entity: Entity<M>;
14
+ collection: EntityCollection<M>;
15
+ path: string;
16
+ className?: string;
17
+ }
18
+
19
+ export function EntityView<M extends Record<string, any>>(
20
+ {
21
+ entity,
22
+ collection,
23
+ path,
24
+ className
25
+ }: EntityViewProps<M>) {
26
+
27
+ const customizationController: CustomizationController = useCustomizationController();
28
+ const resolvedCollection: ResolvedEntityCollection<M> = useMemo(() => resolveCollection<M>({
29
+ collection,
30
+ path,
31
+ entityId: entity.id,
32
+ values: entity.values,
33
+ fields: customizationController.propertyConfigs
34
+ }), [collection, path, entity, customizationController.propertyConfigs]);
35
+
36
+ const properties: ResolvedProperties = resolvedCollection.properties;
37
+
38
+ return (
39
+ <div className={"w-full " + className}>
40
+ <div className={"w-full mb-4"}>
41
+ <div className={cls(defaultBorderMixin, "flex justify-between py-2 border-b last:border-b-0")}>
42
+ <div className="flex items-center w-1/4">
43
+ <span className="pl-2 text-sm text-gray-600">Id</span>
44
+ </div>
45
+ <div
46
+ className="flex-grow p-2 ml-2 w-3/4 text-gray-900 dark:text-white min-h-[56px] flex items-center">
47
+ <span className="flex-grow mr-2">{entity.id}</span>
48
+ {customizationController?.entityLinkBuilder &&
49
+ <a href={customizationController.entityLinkBuilder({ entity })}
50
+ rel="noopener noreferrer"
51
+ target="_blank">
52
+ <IconButton>
53
+ <OpenInNewIcon
54
+ size={"small"}/>
55
+ </IconButton>
56
+ </a>}
57
+ </div>
58
+ </div>
59
+ {Object.entries(properties)
60
+ .map(([key, property]) => {
61
+ const value = entity.values?.[key];
62
+ return (
63
+ <div
64
+ key={`reference_previews_${key}`}
65
+ className={cls(defaultBorderMixin, "flex justify-between py-2 border-b last:border-b-0")}>
66
+ <div className="flex items-center w-1/4">
67
+ <span className="pl-2 text-sm text-gray-600">{property.name}</span>
68
+ </div>
69
+ <div
70
+ className="flex-grow p-2 ml-2 w-3/4 text-gray-900 dark:text-white min-h-[56px] flex items-center">
71
+ <PropertyPreview
72
+ propertyKey={key}
73
+ value={value}
74
+ // entity={entity}
75
+ property={property}
76
+ size={"medium"}/>
77
+ </div>
78
+ </div>
79
+ )
80
+ })}
81
+ </div>
82
+ </div>
83
+ );
84
+ }
@@ -0,0 +1,14 @@
1
+ import React from "react";
2
+ import { Typography } from "@firecms/ui";
3
+
4
+ export function FieldCaption({
5
+ error,
6
+ children
7
+ }: { error?: boolean, children?: React.ReactNode }) {
8
+ if (!children) return null;
9
+ return (
10
+ <Typography variant={"caption"} color={error ? "error" : "secondary"} className={"ml-3.5 mt-0.5"}>
11
+ {children}
12
+ </Typography>
13
+ );
14
+ }
@@ -1,10 +1,10 @@
1
1
  import React from "react";
2
2
 
3
3
  import { Link as ReactLink } from "react-router-dom";
4
- import { ErrorBoundary } from "../components";
4
+ import { ErrorBoundary, FireCMSLogo } from "../components";
5
5
  import {
6
6
  Avatar,
7
- cn,
7
+ cls,
8
8
  DarkModeIcon,
9
9
  IconButton,
10
10
  LightModeIcon,
@@ -18,7 +18,8 @@ import { useAuthController, useLargeLayout, useModeController, useNavigationCont
18
18
  import { User } from "../types";
19
19
 
20
20
  export type FireCMSAppBarProps<ADDITIONAL_PROPS = object> = {
21
- title: string;
21
+
22
+ title?: React.ReactNode;
22
23
  /**
23
24
  * A component that gets rendered on the upper side of the main toolbar
24
25
  */
@@ -30,12 +31,16 @@ export type FireCMSAppBarProps<ADDITIONAL_PROPS = object> = {
30
31
 
31
32
  includeDrawer?: boolean;
32
33
 
34
+ includeModeToggle?: boolean;
35
+
33
36
  drawerOpen: boolean;
34
37
 
35
38
  className?: string;
36
39
 
37
40
  style?: React.CSSProperties;
38
41
 
42
+ logo?: string;
43
+
39
44
  user?: User;
40
45
  } & ADDITIONAL_PROPS;
41
46
 
@@ -55,8 +60,10 @@ export const FireCMSAppBar = function FireCMSAppBar({
55
60
  drawerOpen,
56
61
  dropDownActions,
57
62
  includeDrawer,
63
+ includeModeToggle = true,
58
64
  className,
59
65
  style,
66
+ logo,
60
67
  user: userProp
61
68
  }: FireCMSAppBarProps) {
62
69
  const navigation = useNavigationController();
@@ -89,10 +96,10 @@ export const FireCMSAppBar = function FireCMSAppBar({
89
96
  return (
90
97
  <div
91
98
  style={style}
92
- className={cn("pr-2",
99
+ className={cls("pr-2",
93
100
  {
94
101
  "ml-[17rem]": drawerOpen && largeLayout,
95
- "ml-16": includeDrawer && !(drawerOpen && largeLayout) && !startAdornment,
102
+ "ml-16": includeDrawer && !(drawerOpen && largeLayout),
96
103
  "h-16": true,
97
104
  "z-10": largeLayout,
98
105
  "transition-all": true,
@@ -102,27 +109,37 @@ export const FireCMSAppBar = function FireCMSAppBar({
102
109
  "w-[calc(100%-64px)]": includeDrawer && !(drawerOpen && largeLayout),
103
110
  "w-[calc(100%-17rem)]": includeDrawer && (drawerOpen && largeLayout),
104
111
  "duration-150": drawerOpen && largeLayout,
105
- fixed: true,
112
+ fixed: true
106
113
  },
107
114
  className)}>
108
115
 
109
116
  <div className="flex flex-row gap-2 px-4 h-full items-center">
110
117
 
111
- {startAdornment}
112
-
113
118
  {navigation && <div className="mr-8 hidden lg:block">
114
119
  <ReactLink
115
120
  className="visited:text-inherit visited:dark:text-inherit"
116
121
  to={navigation?.basePath ?? "/"}
117
122
  >
118
- <Typography variant="subtitle1"
119
- noWrap
120
- className={"ml-2 !font-medium"}>
121
- {title}
122
- </Typography>
123
+ <div className={"flex flex-row gap-4"}>
124
+ {!includeDrawer && (logo
125
+ ? <img src={logo}
126
+ alt="Logo"
127
+ className={cls("w-[32px] h-[32px]")}/>
128
+ : <FireCMSLogo width={"32px"} height={"32px"}/>)}
129
+
130
+ {typeof title === "string"
131
+ ? <Typography variant="subtitle1"
132
+ noWrap
133
+ className={"ml-2 !font-medium"}>
134
+ {title}
135
+ </Typography>
136
+ : title}
137
+ </div>
123
138
  </ReactLink>
124
139
  </div>}
125
140
 
141
+ {startAdornment}
142
+
126
143
  <div className={"flex-grow"}/>
127
144
 
128
145
  {endAdornment &&
@@ -130,7 +147,7 @@ export const FireCMSAppBar = function FireCMSAppBar({
130
147
  {endAdornment}
131
148
  </ErrorBoundary>}
132
149
 
133
- <IconButton
150
+ {includeModeToggle && <IconButton
134
151
  color="inherit"
135
152
  aria-label="Open drawer"
136
153
  onClick={toggleMode}
@@ -138,9 +155,17 @@ export const FireCMSAppBar = function FireCMSAppBar({
138
155
  {mode === "dark"
139
156
  ? <DarkModeIcon/>
140
157
  : <LightModeIcon/>}
141
- </IconButton>
158
+ </IconButton>}
142
159
 
143
160
  <Menu trigger={avatarComponent}>
161
+ {user && <div className={"px-4 py-2 mb-2"}>
162
+ {user.displayName && <Typography variant={"body1"} color={"secondary"}>
163
+ {user.displayName}
164
+ </Typography>}
165
+ {user.email && <Typography variant={"body2"} color={"secondary"}>
166
+ {user.email}
167
+ </Typography>}
168
+ </div>}
144
169
 
145
170
  {dropDownActions}
146
171
 
@@ -1,11 +1,11 @@
1
1
  import React, { useCallback, useEffect, useState } from "react";
2
2
 
3
3
  import { useCustomizationController, useFireCMSContext, useNavigationController } from "../../hooks";
4
- import { PluginGenericProps, PluginHomePageAdditionalCardsProps } from "../../types";
4
+ import { CMSAnalyticsEvent, PluginGenericProps, PluginHomePageAdditionalCardsProps } from "../../types";
5
5
 
6
6
  import { toArray } from "../../util/arrays";
7
7
  import { NavigationGroup } from "./NavigationGroup";
8
- import { NavigationCollectionCard } from "./NavigationCollectionCard";
8
+ import { NavigationCardBinding } from "./NavigationCardBinding";
9
9
 
10
10
  // @ts-ignore
11
11
  import * as JsSearch from "js-search";
@@ -102,7 +102,7 @@ export function DefaultHomePage({
102
102
  return (
103
103
  <NavigationGroup
104
104
  group={section.title}
105
- key={`plugin_section_${plugin.name}`}>
105
+ key={`plugin_section_${plugin.key}`}>
106
106
  {section.children}
107
107
  </NavigationGroup>
108
108
  );
@@ -173,21 +173,25 @@ export function DefaultHomePage({
173
173
  <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
174
174
  {thisGroupCollections.map((entry) => (
175
175
  <div key={`nav_${entry.group}_${entry.name}`} className="col-span-1">
176
- <NavigationCollectionCard
176
+ <NavigationCardBinding
177
177
  {...entry}
178
178
  onClick={() => {
179
- const event =
180
- entry.type === "collection"
181
- ? "home_navigate_to_collection"
182
- : entry.type === "view"
183
- ? "home_navigate_to_view"
184
- : "unmapped_event";
179
+ let event: CMSAnalyticsEvent;
180
+ if (entry.type === "collection") {
181
+ event = "home_navigate_to_collection";
182
+ } else if (entry.type === "view") {
183
+ event = "home_navigate_to_view";
184
+ } else if (entry.type === "admin") {
185
+ event = "home_navigate_to_admin_view";
186
+ } else {
187
+ event = "unmapped_event";
188
+ }
185
189
  context.analyticsController?.onAnalyticsEvent?.(event, { path: entry.path });
186
190
  }}
187
191
  />
188
192
  </div>
189
193
  ))}
190
- {AdditionalCards &&
194
+ {group?.toLowerCase() !== "admin" && AdditionalCards &&
191
195
  AdditionalCards.map((AdditionalCard, i) => (
192
196
  <div key={`nav_${group}_add_${i}`}>
193
197
  <AdditionalCard {...actionProps} />
@@ -0,0 +1,69 @@
1
+ import { ArrowForwardIcon, Card, cls, Markdown, Typography, } from "@firecms/ui";
2
+
3
+ export type NavigationCardProps = {
4
+ name: string,
5
+ description?: string;
6
+ actions: React.ReactNode;
7
+ icon: React.ReactNode;
8
+ onClick?: () => void,
9
+ };
10
+
11
+ export function NavigationCard({
12
+ name,
13
+ description,
14
+ icon,
15
+ actions,
16
+ onClick,
17
+ }: NavigationCardProps) {
18
+
19
+ return (<Card
20
+ className={cls("h-full p-4 cursor-pointer min-h-[230px]")}
21
+ onClick={() => {
22
+ onClick?.();
23
+ }}>
24
+
25
+ <div className="flex flex-col items-start h-full">
26
+ <div
27
+ className="flex-grow w-full">
28
+
29
+ <div
30
+ className="h-10 flex items-center w-full justify-between text-gray-300 dark:text-gray-600">
31
+
32
+ {icon}
33
+
34
+ <div
35
+ className="flex items-center gap-1"
36
+ onClick={(event: React.MouseEvent) => {
37
+ event.preventDefault();
38
+ event.stopPropagation();
39
+ }}>
40
+
41
+ {actions}
42
+
43
+ </div>
44
+
45
+ </div>
46
+
47
+ <Typography gutterBottom variant="h5"
48
+ component="h2">
49
+ {name}
50
+ </Typography>
51
+
52
+ {description && <Typography variant="body2"
53
+ color="secondary"
54
+ component="div">
55
+ <Markdown source={description} size={"small"}/>
56
+ </Typography>}
57
+ </div>
58
+
59
+ <div style={{ alignSelf: "flex-end" }}>
60
+
61
+ <div className={"p-4"}>
62
+ <ArrowForwardIcon className="text-primary"/>
63
+ </div>
64
+ </div>
65
+
66
+ </div>
67
+
68
+ </Card>)
69
+ }