@firecms/core 3.0.0-canary.4 → 3.0.0-canary.40

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 (189) 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 +1 -1
  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 +1 -2
  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/StorageItemPreview.d.ts +3 -2
  25. package/dist/form/components/StorageUploadProgress.d.ts +1 -1
  26. package/dist/form/components/index.d.ts +1 -0
  27. package/dist/form/field_bindings/KeyValueFieldBinding.d.ts +1 -1
  28. package/dist/form/field_bindings/MapFieldBinding.d.ts +1 -1
  29. package/dist/form/field_bindings/StorageUploadFieldBinding.d.ts +4 -3
  30. package/dist/form/field_bindings/TextFieldBinding.d.ts +2 -2
  31. package/dist/form/index.d.ts +1 -0
  32. package/dist/form/validation.d.ts +1 -1
  33. package/dist/hooks/data/delete.d.ts +2 -2
  34. package/dist/hooks/data/save.d.ts +1 -1
  35. package/dist/hooks/data/useDataSource.d.ts +2 -2
  36. package/dist/hooks/data/useEntityFetch.d.ts +3 -3
  37. package/dist/hooks/index.d.ts +3 -1
  38. package/dist/{core → hooks}/useBuildModeController.d.ts +1 -1
  39. package/dist/hooks/useBuildNavigationController.d.ts +6 -4
  40. package/dist/hooks/useProjectLog.d.ts +6 -2
  41. package/dist/hooks/useStorageSource.d.ts +2 -2
  42. package/dist/hooks/useValidateAuthenticator.d.ts +25 -0
  43. package/dist/index.es.js +8343 -7846
  44. package/dist/index.es.js.map +1 -1
  45. package/dist/index.umd.js +5 -5
  46. package/dist/index.umd.js.map +1 -1
  47. package/dist/internal/useBuildDataSource.d.ts +4 -0
  48. package/dist/preview/PropertyPreview.d.ts +1 -1
  49. package/dist/preview/PropertyPreviewProps.d.ts +1 -4
  50. package/dist/preview/components/BooleanPreview.d.ts +5 -1
  51. package/dist/preview/components/EnumValuesChip.d.ts +1 -1
  52. package/dist/preview/components/ReferencePreview.d.ts +1 -7
  53. package/dist/types/analytics.d.ts +1 -1
  54. package/dist/types/auth.d.ts +37 -1
  55. package/dist/types/collections.d.ts +22 -5
  56. package/dist/types/datasource.d.ts +1 -1
  57. package/dist/types/entities.d.ts +1 -1
  58. package/dist/types/entity_callbacks.d.ts +2 -2
  59. package/dist/types/entity_overrides.d.ts +6 -0
  60. package/dist/types/index.d.ts +2 -0
  61. package/dist/types/navigation.d.ts +14 -13
  62. package/dist/types/permissions.d.ts +5 -1
  63. package/dist/types/plugins.d.ts +20 -20
  64. package/dist/types/properties.d.ts +2 -2
  65. package/dist/types/property_config.d.ts +2 -2
  66. package/dist/types/roles.d.ts +31 -0
  67. package/dist/types/storage.d.ts +11 -3
  68. package/dist/types/user.d.ts +5 -0
  69. package/dist/util/collections.d.ts +9 -1
  70. package/dist/util/entities.d.ts +1 -1
  71. package/dist/util/icons.d.ts +8 -2
  72. package/dist/util/permissions.d.ts +4 -4
  73. package/dist/util/references.d.ts +4 -2
  74. package/dist/util/resolutions.d.ts +1 -1
  75. package/dist/util/useTraceUpdate.d.ts +1 -0
  76. package/package.json +24 -24
  77. package/src/components/ClearFilterSortButton.tsx +41 -0
  78. package/src/components/DeleteEntityDialog.tsx +4 -4
  79. package/src/components/EntityCollectionTable/EntityCollectionRowActions.tsx +2 -2
  80. package/src/components/EntityCollectionTable/EntityCollectionTable.tsx +268 -277
  81. package/src/components/EntityCollectionTable/EntityCollectionTableProps.tsx +1 -1
  82. package/src/components/EntityCollectionTable/PropertyTableCell.tsx +13 -13
  83. package/src/components/EntityCollectionTable/fields/TableReferenceField.tsx +9 -16
  84. package/src/components/EntityCollectionTable/fields/TableStorageUpload.tsx +3 -3
  85. package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +27 -32
  86. package/src/components/EntityCollectionTable/internal/default_entity_actions.tsx +9 -5
  87. package/src/components/EntityCollectionView/EntityCollectionView.tsx +39 -49
  88. package/src/components/EntityCollectionView/EntityCollectionViewActions.tsx +5 -6
  89. package/src/components/EntityCollectionView/EntityCollectionViewStartActions.tsx +68 -0
  90. package/src/components/EntityCollectionView/useSelectionController.tsx +30 -0
  91. package/src/components/EntityPreview.tsx +207 -70
  92. package/src/components/EntityView.tsx +84 -0
  93. package/src/components/FieldCaption.tsx +14 -0
  94. package/src/components/FireCMSAppBar.tsx +8 -0
  95. package/src/components/HomePage/DefaultHomePage.tsx +14 -10
  96. package/src/components/HomePage/NavigationCard.tsx +69 -0
  97. package/src/components/HomePage/NavigationCardBinding.tsx +116 -0
  98. package/src/components/HomePage/SmallNavigationCard.tsx +45 -0
  99. package/src/components/HomePage/index.tsx +3 -1
  100. package/src/components/ReferenceTable/ReferenceSelectionTable.tsx +3 -4
  101. package/src/components/ReferenceWidget.tsx +4 -4
  102. package/src/components/SelectableTable/filters/DateTimeFilterField.tsx +23 -8
  103. package/src/components/SelectableTable/filters/ReferenceFilterField.tsx +35 -24
  104. package/src/components/SelectableTable/filters/StringNumberFilterField.tsx +35 -15
  105. package/src/components/VirtualTable/VirtualTableProps.tsx +1 -1
  106. package/src/components/VirtualTable/fields/VirtualTableDateField.tsx +1 -1
  107. package/src/components/common/useDataSourceEntityCollectionTableController.tsx +1 -1
  108. package/src/components/index.tsx +4 -3
  109. package/src/contexts/AuthControllerContext.tsx +1 -1
  110. package/src/core/Drawer.tsx +66 -39
  111. package/src/{internal/EntityView.tsx → core/EntityEditView.tsx} +22 -39
  112. package/src/core/EntitySidePanel.tsx +2 -2
  113. package/src/core/FireCMS.tsx +18 -2
  114. package/src/core/NavigationRoutes.tsx +8 -0
  115. package/src/core/SideEntityView.tsx +38 -0
  116. package/src/core/field_configs.tsx +1 -2
  117. package/src/core/index.tsx +0 -2
  118. package/src/form/EntityForm.tsx +20 -12
  119. package/src/form/components/StorageItemPreview.tsx +5 -3
  120. package/src/form/components/StorageUploadProgress.tsx +6 -5
  121. package/src/form/components/index.tsx +1 -0
  122. package/src/form/field_bindings/ArrayCustomShapedFieldBinding.tsx +2 -3
  123. package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +12 -15
  124. package/src/form/field_bindings/BlockFieldBinding.tsx +2 -3
  125. package/src/form/field_bindings/DateTimeFieldBinding.tsx +4 -4
  126. package/src/form/field_bindings/KeyValueFieldBinding.tsx +18 -18
  127. package/src/form/field_bindings/MapFieldBinding.tsx +17 -17
  128. package/src/form/field_bindings/MarkdownFieldBinding.tsx +1 -2
  129. package/src/form/field_bindings/MultiSelectBinding.tsx +2 -3
  130. package/src/form/field_bindings/ReadOnlyFieldBinding.tsx +3 -3
  131. package/src/form/field_bindings/ReferenceFieldBinding.tsx +5 -3
  132. package/src/form/field_bindings/RepeatFieldBinding.tsx +3 -3
  133. package/src/form/field_bindings/SelectFieldBinding.tsx +2 -3
  134. package/src/form/field_bindings/StorageUploadFieldBinding.tsx +15 -6
  135. package/src/form/field_bindings/SwitchFieldBinding.tsx +2 -3
  136. package/src/form/field_bindings/TextFieldBinding.tsx +10 -9
  137. package/src/form/index.tsx +1 -0
  138. package/src/form/validation.ts +3 -4
  139. package/src/hooks/data/delete.ts +3 -3
  140. package/src/hooks/data/save.ts +1 -1
  141. package/src/hooks/data/useCollectionFetch.tsx +1 -1
  142. package/src/hooks/data/useDataSource.tsx +8 -3
  143. package/src/hooks/data/useEntityFetch.tsx +4 -4
  144. package/src/hooks/index.tsx +5 -1
  145. package/src/{core → hooks}/useBuildLocalConfigurationPersistence.tsx +9 -10
  146. package/src/{core → hooks}/useBuildModeController.tsx +12 -6
  147. package/src/hooks/useBuildNavigationController.tsx +190 -72
  148. package/src/hooks/useProjectLog.tsx +16 -6
  149. package/src/hooks/useReferenceDialog.tsx +2 -2
  150. package/src/hooks/useStorageSource.tsx +7 -2
  151. package/src/hooks/useValidateAuthenticator.tsx +135 -0
  152. package/src/internal/useBuildDataSource.ts +6 -1
  153. package/src/internal/useBuildSideEntityController.tsx +18 -12
  154. package/src/preview/PropertyPreview.tsx +1 -1
  155. package/src/preview/PropertyPreviewProps.tsx +1 -11
  156. package/src/preview/components/BooleanPreview.tsx +19 -4
  157. package/src/preview/components/EnumValuesChip.tsx +1 -1
  158. package/src/preview/components/ReferencePreview.tsx +55 -147
  159. package/src/preview/property_previews/StringPropertyPreview.tsx +8 -7
  160. package/src/types/analytics.ts +1 -0
  161. package/src/types/auth.tsx +50 -1
  162. package/src/types/collections.ts +24 -5
  163. package/src/types/datasource.ts +1 -1
  164. package/src/types/entities.ts +1 -1
  165. package/src/types/entity_actions.tsx +4 -0
  166. package/src/types/entity_callbacks.ts +2 -2
  167. package/src/types/entity_overrides.tsx +7 -0
  168. package/src/types/firecms.tsx +0 -1
  169. package/src/types/index.ts +2 -0
  170. package/src/types/navigation.ts +17 -16
  171. package/src/types/permissions.ts +6 -1
  172. package/src/types/plugins.tsx +26 -28
  173. package/src/types/properties.ts +3 -2
  174. package/src/types/property_config.tsx +2 -2
  175. package/src/types/roles.ts +41 -0
  176. package/src/types/side_entity_controller.tsx +1 -0
  177. package/src/types/storage.ts +12 -3
  178. package/src/types/user.ts +7 -0
  179. package/src/util/collections.ts +22 -0
  180. package/src/util/entities.ts +1 -1
  181. package/src/util/icons.tsx +11 -3
  182. package/src/util/permissions.ts +11 -8
  183. package/src/util/references.ts +36 -5
  184. package/src/util/strings.ts +2 -2
  185. package/src/util/useTraceUpdate.tsx +2 -1
  186. package/src/components/HomePage/NavigationCollectionCard.tsx +0 -146
  187. /package/dist/{components → form/components}/LabelWithIcon.d.ts +0 -0
  188. /package/dist/{core → hooks}/useBuildLocalConfigurationPersistence.d.ts +0 -0
  189. /package/src/{components → form/components}/LabelWithIcon.tsx +0 -0
@@ -0,0 +1,45 @@
1
+ import { ArrowForwardIcon, cardClickableMixin, cardMixin, cn, focusedMixin, Typography, } from "@firecms/ui";
2
+
3
+ import { Link as ReactLink } from "react-router-dom";
4
+
5
+ export type SmallNavigationCardProps = {
6
+ name: string,
7
+ url: string;
8
+ icon: React.ReactElement;
9
+ };
10
+
11
+ export function SmallNavigationCard({
12
+ name,
13
+ url,
14
+ icon,
15
+ }: SmallNavigationCardProps) {
16
+
17
+ return (
18
+ <>
19
+
20
+ <ReactLink
21
+ tabIndex={0}
22
+ className={cn(cardMixin,
23
+ cardClickableMixin,
24
+ focusedMixin,
25
+ "cursor-pointer flex flex-row items-center px-4 py-2 text-inherit dark:text-inherit visited:text-inherit visited:dark:text-inherit hover:text-inherit hover:dark:text-inherit ")}
26
+ to={url}
27
+ >
28
+
29
+ <div className="flex flex-row items-center flex-grow gap-2 ">
30
+ {icon}
31
+
32
+ <Typography gutterBottom variant="h5"
33
+ component="h2"
34
+ className="mb-0 ml-4">
35
+ {name}
36
+ </Typography>
37
+ </div>
38
+
39
+ <div className={"p-4"}>
40
+ <ArrowForwardIcon color="primary"/>
41
+ </div>
42
+ </ReactLink>
43
+
44
+ </>);
45
+ }
@@ -1,3 +1,5 @@
1
1
  export * from "./DefaultHomePage";
2
- export * from "./NavigationCollectionCard";
2
+ export * from "./NavigationCardBinding";
3
3
  export * from "./NavigationGroup";
4
+ export * from "./NavigationCard";
5
+ export * from "./SmallNavigationCard";
@@ -17,7 +17,7 @@ import {
17
17
  import { ErrorView } from "../ErrorView";
18
18
  import { AddIcon, Button, DialogActions, Typography } from "@firecms/ui";
19
19
  import { canCreateEntity, fullPathToCollectionSegments, resolveCollection } from "../../util";
20
- import { useSelectionController } from "../EntityCollectionView/EntityCollectionView";
20
+ import { useSelectionController } from "../EntityCollectionView/useSelectionController";
21
21
  import { useColumnIds, useTableSearchHelper } from "../common";
22
22
  import { useSideDialogContext } from "../../core";
23
23
  import { useAnalyticsController } from "../../hooks/useAnalyticsController";
@@ -109,7 +109,7 @@ export function ReferenceSelectionTable<M extends Record<string, any>>(
109
109
 
110
110
  const fullPath = navigation.resolveAliasesFrom(pathInput);
111
111
 
112
- const dataSource = useDataSource();
112
+ const dataSource = useDataSource(collection);
113
113
 
114
114
  const [entitiesDisplayedFirst, setEntitiesDisplayedFirst] = useState<Entity<any>[]>([]);
115
115
 
@@ -349,7 +349,7 @@ function ReferenceDialogActions({
349
349
  onNewClick();
350
350
  }
351
351
  : undefined;
352
- const addButton = canCreateEntity(collection, authController, fullPathToCollectionSegments(path), null) &&
352
+ const addButton = canCreateEntity(collection, authController, path, null) &&
353
353
  onClick && (largeLayout
354
354
  ? <Button
355
355
  onClick={onClick}
@@ -360,7 +360,6 @@ function ReferenceDialogActions({
360
360
  </Button>
361
361
  : <Button
362
362
  onClick={onClick}
363
- size="medium"
364
363
  variant="outlined"
365
364
  color="primary"
366
365
  >
@@ -50,11 +50,11 @@ export function ReferenceWidget<M extends Record<string, any>>({
50
50
 
51
51
  const collection: EntityCollection | undefined = useMemo(() => {
52
52
  return navigationController.getCollection(path);
53
- }, [path, navigationController]);
53
+ }, [path, navigationController.getCollection]);
54
54
 
55
- if (!collection) {
56
- throw Error(`Couldn't find the corresponding collection for the path: ${path}`);
57
- }
55
+ // if (!collection) {
56
+ // throw Error(`Couldn't find the corresponding collection for the path: ${path}`);
57
+ // }
58
58
 
59
59
  const onSingleEntitySelected = useCallback((entity: Entity<M> | null) => {
60
60
  if (disabled)
@@ -1,6 +1,6 @@
1
1
  import React, { useState } from "react";
2
2
  import { VirtualTableWhereFilterOp } from "../../VirtualTable";
3
- import { DateTimeField, Select, SelectItem } from "@firecms/ui";
3
+ import { Checkbox, DateTimeField, Label, Select, SelectItem } from "@firecms/ui";
4
4
  import { useCustomizationController } from "../../../hooks";
5
5
 
6
6
  interface DateTimeFilterFieldProps {
@@ -43,10 +43,10 @@ export function DateTimeFilterField({
43
43
 
44
44
  const [fieldOperation, fieldValue] = value || [possibleOperations[0], undefined];
45
45
  const [operation, setOperation] = useState<VirtualTableWhereFilterOp>(fieldOperation);
46
- const [internalValue, setInternalValue] = useState<Date | undefined>(fieldValue);
46
+ const [internalValue, setInternalValue] = useState<Date | null | undefined>(fieldValue);
47
47
 
48
- function updateFilter(op: VirtualTableWhereFilterOp, val: Date | undefined) {
49
- let newValue: Date | undefined = val;
48
+ function updateFilter(op: VirtualTableWhereFilterOp, val: Date | undefined | null) {
49
+ let newValue: Date | null | undefined = val;
50
50
  const prevOpIsArray = multipleSelectOperations.includes(operation);
51
51
  const newOpIsArray = multipleSelectOperations.includes(op);
52
52
  if (prevOpIsArray !== newOpIsArray) {
@@ -73,7 +73,7 @@ export function DateTimeFilterField({
73
73
 
74
74
  return (
75
75
 
76
- <div className="flex w-[440px] items-center">
76
+ <div className="flex w-[440px]">
77
77
  <div className="w-[80px]">
78
78
  <Select value={operation}
79
79
  onValueChange={(value) => {
@@ -88,19 +88,34 @@ export function DateTimeFilterField({
88
88
  </Select>
89
89
  </div>
90
90
 
91
- <div className="flex-grow ml-2">
91
+ <div className="flex-grow ml-2 flex flex-col gap-2">
92
92
 
93
93
  <DateTimeField
94
94
  mode={mode}
95
95
  size={"medium"}
96
96
  locale={locale}
97
- value={internalValue}
98
- onChange={(dateValue: Date | null) => {
97
+ value={internalValue ?? undefined}
98
+ onChange={(dateValue: Date | undefined) => {
99
99
  updateFilter(operation, dateValue === null ? undefined : dateValue);
100
100
  }}
101
101
  clearable={true}
102
102
  />
103
103
 
104
+ <Label
105
+ className="border cursor-pointer rounded-md p-2 flex items-center gap-2 [&:has(:checked)]:bg-gray-100 dark:[&:has(:checked)]:bg-gray-800"
106
+ htmlFor="null-filter"
107
+ >
108
+ <Checkbox id="null-filter"
109
+ checked={internalValue === null}
110
+ size={"small"}
111
+ onCheckedChange={(checked) => {
112
+ if (internalValue !== null)
113
+ updateFilter(operation, null);
114
+ else updateFilter(operation, undefined);
115
+ }}/>
116
+ Filter for null values
117
+ </Label>
118
+
104
119
  </div>
105
120
 
106
121
  </div>
@@ -4,7 +4,7 @@ import { Entity, EntityCollection, EntityReference } from "../../../types";
4
4
  import { ReferencePreview } from "../../../preview";
5
5
  import { getReferenceFrom } from "../../../util";
6
6
  import { useNavigationController, useReferenceDialog } from "../../../hooks";
7
- import { Button, Select, SelectItem } from "@firecms/ui";
7
+ import { Button, Checkbox, Label, Select, SelectItem } from "@firecms/ui";
8
8
 
9
9
  interface ReferenceFilterFieldProps {
10
10
  name: string,
@@ -48,33 +48,31 @@ export function ReferenceFilterField({
48
48
  ? ["array-contains"]
49
49
  : ["==", "!=", ">", "<", ">=", "<="];
50
50
 
51
- const [onHover, setOnHover] = React.useState(false);
52
-
53
51
  isArray
54
52
  ? possibleOperations.push("array-contains-any")
55
53
  : possibleOperations.push("in", "not-in");
56
54
 
57
55
  const [fieldOperation, fieldValue] = value || [possibleOperations[0], undefined];
58
56
  const [operation, setOperation] = useState<VirtualTableWhereFilterOp>(fieldOperation);
59
- const [internalValue, setInternalValue] = useState<EntityReference | EntityReference[] | undefined>(fieldValue);
57
+ const [internalValue, setInternalValue] = useState<EntityReference | EntityReference[] | undefined | null>(fieldValue);
60
58
 
61
59
  const selectedEntityIds = internalValue
62
60
  ? (Array.isArray(internalValue) ? internalValue.map((ref) => {
63
- if (!(ref.isEntityReference && ref.isEntityReference())) {
61
+ if (!(ref?.isEntityReference && ref?.isEntityReference())) {
64
62
  return null;
65
63
  }
66
64
  return ref.id;
67
65
  }).filter(Boolean) as string[] : [internalValue.id])
68
66
  : [];
69
67
 
70
- function updateFilter(op: VirtualTableWhereFilterOp, val?: EntityReference | EntityReference[]) {
68
+ function updateFilter(op: VirtualTableWhereFilterOp, val?: EntityReference | EntityReference[] | null) {
71
69
 
72
70
  const prevOpIsArray = multipleSelectOperations.includes(operation);
73
71
  const newOpIsArray = multipleSelectOperations.includes(op);
74
72
  let newValue = val;
75
73
  if (prevOpIsArray !== newOpIsArray) {
76
74
  // @ts-ignore
77
- newValue = newOpIsArray ? (newValue.isEntityReference && newValue.isEntityReference() ? [newValue] : []) : undefined
75
+ newValue = newOpIsArray ? (newValue?.isEntityReference && newValue?.isEntityReference() ? [newValue] : []) : undefined
78
76
  }
79
77
 
80
78
  setOperation(op);
@@ -129,28 +127,22 @@ export function ReferenceFilterField({
129
127
 
130
128
  const buildEntry = (reference: EntityReference) => {
131
129
  return (
132
- <div
133
- className="mb-0.5"
134
- onMouseEnter={() => setOnHover(true)}
135
- onMouseMove={() => setOnHover(true)}
136
- onMouseLeave={() => setOnHover(false)}>
137
- <ReferencePreview
138
- disabled={!path}
139
- previewProperties={previewProperties}
140
- size={"medium"}
141
- onClick={doOpenDialog}
142
- reference={reference}
143
- onHover={onHover}
144
- allowEntityNavigation={false}
145
- />
146
- </div>
130
+ <ReferencePreview
131
+ disabled={!path}
132
+ previewProperties={previewProperties}
133
+ size={"medium"}
134
+ onClick={doOpenDialog}
135
+ reference={reference}
136
+ hover={true}
137
+ allowEntityNavigation={false}
138
+ />
147
139
  );
148
140
  };
149
141
 
150
142
  return (
151
143
 
152
144
  <div className="flex w-[440px] flex-row">
153
- <div className="w-[120px]">
145
+ <div className="w-[140px]">
154
146
  <Select value={operation}
155
147
  onValueChange={(value) => {
156
148
  updateFilter(value as VirtualTableWhereFilterOp, internalValue);
@@ -164,21 +156,40 @@ export function ReferenceFilterField({
164
156
  </Select>
165
157
  </div>
166
158
 
167
- <div className="flex-grow ml-2 h-full">
159
+ <div className="flex-grow ml-2 h-full gap-2 flex flex-col">
168
160
 
169
161
  {internalValue && Array.isArray(internalValue) && <div>
170
162
  {internalValue.map((ref, index) => buildEntry(ref))}
171
163
  </div>}
164
+
172
165
  {internalValue && !Array.isArray(internalValue) && <div>
173
166
  {buildEntry(internalValue)}
174
167
  </div>}
168
+
175
169
  {(!internalValue || (Array.isArray(internalValue) && internalValue.length === 0)) &&
176
170
  <Button onClick={doOpenDialog}
177
171
  variant={"outlined"}
172
+ size={"large"}
178
173
  className="h-full w-full">
179
174
  {multiple ? "Select references" : "Select reference"}
180
175
  </Button>
181
176
  }
177
+
178
+ {!isArray && <Label
179
+ className="border cursor-pointer rounded-md p-2 flex items-center gap-2 [&:has(:checked)]:bg-gray-100 dark:[&:has(:checked)]:bg-gray-800"
180
+ htmlFor="null-filter"
181
+ >
182
+ <Checkbox id="null-filter"
183
+ checked={internalValue === null}
184
+ size={"small"}
185
+ onCheckedChange={(checked) => {
186
+ if (internalValue !== null)
187
+ updateFilter(operation, null);
188
+ else updateFilter(operation, undefined);
189
+ }}/>
190
+ Filter for null values
191
+ </Label>}
192
+
182
193
  </div>
183
194
 
184
195
  </div>
@@ -1,7 +1,7 @@
1
1
  import React, { useState } from "react";
2
2
  import { EnumValuesChip } from "../../../preview";
3
3
  import { VirtualTableWhereFilterOp } from "../../VirtualTable";
4
- import { ClearIcon, IconButton, Select, SelectItem, TextField } from "@firecms/ui";
4
+ import { Checkbox, ClearIcon, IconButton, Label, Select, SelectItem, TextField } from "@firecms/ui";
5
5
  import { EnumValueConfig } from "../../../types";
6
6
 
7
7
  interface StringNumberFilterFieldProps {
@@ -50,15 +50,15 @@ export function StringNumberFilterField({
50
50
 
51
51
  const [fieldOperation, fieldValue] = value || [possibleOperations[0], undefined];
52
52
  const [operation, setOperation] = useState<VirtualTableWhereFilterOp>(fieldOperation);
53
- const [internalValue, setInternalValue] = useState<string | number | string[] | number[] | undefined>(fieldValue);
53
+ const [internalValue, setInternalValue] = useState<string | number | string[] | number[] | null | undefined>(fieldValue);
54
54
 
55
- function updateFilter(op: VirtualTableWhereFilterOp, val: string | number | string[] | number[] | undefined) {
55
+ function updateFilter(op: VirtualTableWhereFilterOp, val: string | number | string[] | number[] | null | undefined) {
56
56
  let newValue = val;
57
57
  const prevOpIsArray = multipleSelectOperations.includes(operation);
58
58
  const newOpIsArray = multipleSelectOperations.includes(op);
59
59
  if (prevOpIsArray !== newOpIsArray) {
60
60
  // @ts-ignore
61
- newValue = newOpIsArray ? (typeof val === "string" || typeof val === "number" ? [val] : []) : "";
61
+ newValue = newOpIsArray ? (typeof val === "string" || typeof val === "number" ? [val] : []) : undefined;
62
62
  }
63
63
 
64
64
  if (typeof newValue === "number" && isNaN(newValue))
@@ -84,7 +84,7 @@ export function StringNumberFilterField({
84
84
  const multiple = multipleSelectOperations.includes(operation);
85
85
  return (
86
86
 
87
- <div className="flex w-[440px] items-center">
87
+ <div className="flex w-[440px]">
88
88
  <div className={"w-[80px]"}>
89
89
  <Select value={operation}
90
90
  position={"item-aligned"}
@@ -100,11 +100,11 @@ export function StringNumberFilterField({
100
100
  </Select>
101
101
  </div>
102
102
 
103
- <div className="flex-grow ml-2">
103
+ <div className="flex-grow ml-2 flex flex-col gap-2">
104
104
 
105
105
  {!enumValues && <TextField
106
106
  type={dataType === "number" ? "number" : undefined}
107
- value={internalValue !== undefined ? String(internalValue) : ""}
107
+ value={internalValue !== undefined && internalValue != null ? String(internalValue) : ""}
108
108
  onChange={(evt) => {
109
109
  const val = dataType === "number"
110
110
  ? parseFloat(evt.target.value)
@@ -118,26 +118,31 @@ export function StringNumberFilterField({
118
118
  />}
119
119
 
120
120
  {enumValues &&
121
-
122
121
  <Select
123
122
  position={"item-aligned"}
124
123
  value={internalValue !== undefined
125
124
  ? (Array.isArray(internalValue) ? internalValue.map(e => String(e)) : String(internalValue))
126
125
  : isArray ? [] : ""}
127
126
  onValueChange={(value) => {
128
- updateFilter(operation, dataType === "number" ? parseInt(value as string) : value as string)
127
+ if (value !== "")
128
+ updateFilter(operation, dataType === "number" ? parseInt(value as string) : value as string)
129
129
  }}
130
130
  multiple={multiple}
131
131
  endAdornment={internalValue && <IconButton
132
- className="absolute right-3 top-2"
132
+ className="absolute right-2 top-3"
133
133
  onClick={(e) => updateFilter(operation, undefined)}>
134
134
  <ClearIcon/>
135
135
  </IconButton>}
136
- renderValue={(enumKey) => <EnumValuesChip
137
- key={`select_value_${name}_${enumKey}`}
138
- enumKey={enumKey}
139
- enumValues={enumValues}
140
- size={"small"}/>}>
136
+ renderValue={(enumKey) => {
137
+ if (enumKey === null)
138
+ return "Filter for null values";
139
+
140
+ return <EnumValuesChip
141
+ key={`select_value_${name}_${enumKey}`}
142
+ enumKey={enumKey}
143
+ enumValues={enumValues}
144
+ size={"small"}/>;
145
+ }}>
141
146
  {enumValues.map((enumConfig) => (
142
147
  <SelectItem key={`select_value_${name}_${enumConfig.id}`}
143
148
  value={String(enumConfig.id)}>
@@ -150,6 +155,21 @@ export function StringNumberFilterField({
150
155
  </Select>
151
156
  }
152
157
 
158
+ {!isArray && <Label
159
+ className="border cursor-pointer rounded-md p-2 flex items-center gap-2 [&:has(:checked)]:bg-gray-100 dark:[&:has(:checked)]:bg-gray-800"
160
+ htmlFor="null-filter"
161
+ >
162
+ <Checkbox id="null-filter"
163
+ checked={internalValue === null}
164
+ size={"small"}
165
+ onCheckedChange={(checked) => {
166
+ if (internalValue !== null)
167
+ updateFilter(operation, null);
168
+ else updateFilter(operation, undefined);
169
+ }}/>
170
+ Filter for null values
171
+ </Label>}
172
+
153
173
  </div>
154
174
 
155
175
  </div>
@@ -252,7 +252,7 @@ export type VirtualTableFilterValues<Key extends string> = Partial<Record<Key, [
252
252
 
253
253
  /**
254
254
  * Filter conditions in a `Query.where()` clause are specified using the
255
- * strings '<', '<=', '==', '>=', '>', 'array-contains', 'in', 'not-in', and 'array-contains-any'.
255
+ * strings `<`, `<=`, `==`, `>=`, `>`, `array-contains`, `in`, and `array-contains-any`.
256
256
  * @see Table
257
257
  * @group Models
258
258
  */
@@ -25,7 +25,7 @@ export function VirtualTableDateField(props: {
25
25
  return (
26
26
  <DateTimeField
27
27
  value={internalValue ?? undefined}
28
- onChange={(dateValue) => updateValue(dateValue)}
28
+ onChange={(dateValue) => updateValue(dateValue ?? null)}
29
29
  size={"medium"}
30
30
  invisible={true}
31
31
  className={"w-full h-full"}
@@ -65,7 +65,7 @@ export function useDataSourceEntityCollectionTableController<M extends Record<st
65
65
 
66
66
  const [popupCell, setPopupCell] = React.useState<SelectedCellProps<M> | undefined>(undefined);
67
67
  const navigation = useNavigationController();
68
- const dataSource = useDataSource();
68
+ const dataSource = useDataSource(collection);
69
69
  const resolvedPath = useMemo(() => navigation.resolveAliasesFrom(fullPath), [fullPath, navigation.resolveAliasesFrom]);
70
70
 
71
71
  const forceFilter = forceFilterFromProps ?? forceFilterFromCollection;
@@ -1,8 +1,8 @@
1
1
  export type { ErrorViewProps } from "./ErrorView";
2
2
  export { ErrorView } from "./ErrorView";
3
3
 
4
- export type { EntityPreviewProps } from "./EntityPreview";
5
- export { EntityPreview } from "./EntityPreview";
4
+ export type { EntityViewProps } from "./EntityView";
5
+ export { EntityView } from "./EntityView";
6
6
 
7
7
  export type { ReferenceSelectionInnerProps } from "./ReferenceTable/ReferenceSelectionTable";
8
8
  export { ReferenceSelectionTable } from "./ReferenceTable/ReferenceSelectionTable";
@@ -15,6 +15,7 @@ export * from "./HomePage";
15
15
  export * from "./SelectableTable/SelectableTable";
16
16
  export * from "./EntityCollectionView/EntityCollectionView";
17
17
  export * from "./EntityCollectionView/EntityCollectionViewActions";
18
+ export * from "./EntityCollectionView/useSelectionController";
18
19
 
19
20
  export * from "./PropertyConfigBadge";
20
21
 
@@ -31,5 +32,5 @@ export * from "./FireCMSAppBar";
31
32
 
32
33
  export * from "./ArrayContainer";
33
34
  export * from "./ReferenceWidget";
34
- export * from "./LabelWithIcon";
35
35
  export * from "./SearchIconsView";
36
+ export * from "./FieldCaption";
@@ -1,4 +1,4 @@
1
1
  import React from "react";
2
2
  import { AuthController } from "../types";
3
3
 
4
- export const AuthControllerContext = React.createContext<AuthController>({} as AuthController);
4
+ export const AuthControllerContext = React.createContext<AuthController<any, any>>({} as AuthController<any, any>);
@@ -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",