@firecms/core 3.0.0-beta.4.pre.2 → 3.0.0-beta.6

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 (109) hide show
  1. package/README.md +1 -1
  2. package/dist/components/ClearFilterSortButton.d.ts +5 -0
  3. package/dist/components/EntityCollectionTable/EntityCollectionRowActions.d.ts +11 -11
  4. package/dist/components/EntityCollectionTable/EntityCollectionTableProps.d.ts +1 -1
  5. package/dist/components/EntityCollectionTable/internal/CollectionTableToolbar.d.ts +1 -4
  6. package/dist/components/EntityCollectionView/EntityCollectionView.d.ts +11 -1
  7. package/dist/components/EntityCollectionView/EntityCollectionViewStartActions.d.ts +11 -0
  8. package/dist/components/EntityPreview.d.ts +2 -2
  9. package/dist/components/SelectableTable/SelectableTable.d.ts +1 -1
  10. package/dist/components/common/types.d.ts +1 -1
  11. package/dist/contexts/AuthControllerContext.d.ts +1 -1
  12. package/dist/form/components/ErrorFocus.d.ts +1 -1
  13. package/dist/hooks/data/delete.d.ts +2 -2
  14. package/dist/hooks/data/save.d.ts +1 -1
  15. package/dist/hooks/data/useDataSource.d.ts +1 -1
  16. package/dist/hooks/data/useEntityFetch.d.ts +3 -3
  17. package/dist/hooks/index.d.ts +1 -0
  18. package/dist/hooks/useBuildNavigationController.d.ts +1 -2
  19. package/dist/hooks/useProjectLog.d.ts +2 -2
  20. package/dist/hooks/useValidateAuthenticator.d.ts +21 -0
  21. package/dist/index.es.js +3948 -3737
  22. package/dist/index.es.js.map +1 -1
  23. package/dist/index.umd.js +5 -5
  24. package/dist/index.umd.js.map +1 -1
  25. package/dist/internal/useBuildDataSource.d.ts +1 -16
  26. package/dist/internal/useRestoreScroll.d.ts +1 -1
  27. package/dist/preview/PropertyPreviewProps.d.ts +1 -4
  28. package/dist/types/auth.d.ts +30 -1
  29. package/dist/types/collections.d.ts +8 -4
  30. package/dist/types/datasource.d.ts +3 -6
  31. package/dist/types/entities.d.ts +5 -1
  32. package/dist/types/entity_callbacks.d.ts +2 -2
  33. package/dist/types/index.d.ts +1 -1
  34. package/dist/types/navigation.d.ts +4 -4
  35. package/dist/types/plugins.d.ts +5 -3
  36. package/dist/types/properties.d.ts +2 -2
  37. package/dist/types/roles.d.ts +31 -0
  38. package/dist/types/storage.d.ts +11 -3
  39. package/dist/types/user.d.ts +5 -0
  40. package/dist/util/collections.d.ts +1 -1
  41. package/dist/util/entities.d.ts +1 -1
  42. package/dist/util/navigation_utils.d.ts +2 -2
  43. package/dist/util/resolutions.d.ts +5 -5
  44. package/dist/util/useTraceUpdate.d.ts +1 -0
  45. package/package.json +23 -20
  46. package/src/components/ClearFilterSortButton.tsx +41 -0
  47. package/src/components/EntityCollectionTable/EntityCollectionTable.tsx +10 -11
  48. package/src/components/EntityCollectionTable/EntityCollectionTableProps.tsx +1 -1
  49. package/src/components/EntityCollectionTable/PropertyTableCell.tsx +16 -19
  50. package/src/components/EntityCollectionTable/fields/TableReferenceField.tsx +1 -1
  51. package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +27 -32
  52. package/src/components/EntityCollectionTable/internal/EntityTableCell.tsx +11 -6
  53. package/src/components/EntityCollectionView/EntityCollectionView.tsx +28 -5
  54. package/src/components/EntityCollectionView/EntityCollectionViewStartActions.tsx +68 -0
  55. package/src/components/EntityPreview.tsx +5 -2
  56. package/src/components/EntityView.tsx +1 -1
  57. package/src/components/HomePage/DefaultHomePage.tsx +2 -2
  58. package/src/components/HomePage/NavigationCard.tsx +1 -1
  59. package/src/components/ReferenceWidget.tsx +1 -1
  60. package/src/components/SelectableTable/SelectableTable.tsx +1 -1
  61. package/src/components/SelectableTable/filters/BooleanFilterField.tsx +2 -3
  62. package/src/components/SelectableTable/filters/DateTimeFilterField.tsx +23 -8
  63. package/src/components/SelectableTable/filters/ReferenceFilterField.tsx +24 -5
  64. package/src/components/SelectableTable/filters/StringNumberFilterField.tsx +35 -15
  65. package/src/components/VirtualTable/VirtualTable.tsx +28 -20
  66. package/src/components/VirtualTable/fields/VirtualTableDateField.tsx +1 -1
  67. package/src/components/common/types.tsx +1 -1
  68. package/src/contexts/AuthControllerContext.tsx +1 -1
  69. package/src/core/FireCMS.tsx +2 -3
  70. package/src/core/field_configs.tsx +1 -2
  71. package/src/form/EntityForm.tsx +1 -1
  72. package/src/form/field_bindings/DateTimeFieldBinding.tsx +1 -1
  73. package/src/hooks/data/delete.ts +3 -3
  74. package/src/hooks/data/save.ts +2 -1
  75. package/src/hooks/data/useDataSource.tsx +1 -1
  76. package/src/hooks/data/useEntityFetch.tsx +3 -3
  77. package/src/hooks/index.tsx +2 -0
  78. package/src/hooks/useBuildLocalConfigurationPersistence.tsx +9 -10
  79. package/src/hooks/useBuildModeController.tsx +11 -5
  80. package/src/hooks/useBuildNavigationController.tsx +136 -59
  81. package/src/hooks/useProjectLog.tsx +8 -6
  82. package/src/hooks/useValidateAuthenticator.tsx +115 -0
  83. package/src/internal/useBuildDataSource.ts +42 -47
  84. package/src/internal/useBuildSideEntityController.tsx +18 -12
  85. package/src/preview/PropertyPreview.tsx +2 -12
  86. package/src/preview/PropertyPreviewProps.tsx +1 -11
  87. package/src/preview/components/BooleanPreview.tsx +4 -2
  88. package/src/preview/components/ReferencePreview.tsx +1 -1
  89. package/src/types/auth.tsx +40 -1
  90. package/src/types/collections.ts +8 -4
  91. package/src/types/datasource.ts +8 -5
  92. package/src/types/entities.ts +9 -1
  93. package/src/types/entity_callbacks.ts +2 -2
  94. package/src/types/index.ts +1 -1
  95. package/src/types/navigation.ts +6 -6
  96. package/src/types/plugins.tsx +6 -5
  97. package/src/types/properties.ts +5 -4
  98. package/src/types/roles.ts +41 -0
  99. package/src/types/storage.ts +12 -3
  100. package/src/types/user.ts +7 -0
  101. package/src/util/collections.ts +1 -1
  102. package/src/util/entities.ts +1 -1
  103. package/src/util/navigation_utils.ts +6 -6
  104. package/src/util/strings.ts +2 -2
  105. package/src/util/useTraceUpdate.tsx +2 -1
  106. package/dist/internal/useLocaleConfig.d.ts +0 -1
  107. package/dist/types/appcheck.d.ts +0 -26
  108. package/src/internal/useLocaleConfig.tsx +0 -18
  109. package/src/types/appcheck.ts +0 -29
@@ -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,
@@ -54,7 +54,7 @@ export function ReferenceFilterField({
54
54
 
55
55
  const [fieldOperation, fieldValue] = value || [possibleOperations[0], undefined];
56
56
  const [operation, setOperation] = useState<VirtualTableWhereFilterOp>(fieldOperation);
57
- const [internalValue, setInternalValue] = useState<EntityReference | EntityReference[] | undefined>(fieldValue);
57
+ const [internalValue, setInternalValue] = useState<EntityReference | EntityReference[] | undefined | null>(fieldValue);
58
58
 
59
59
  const selectedEntityIds = internalValue
60
60
  ? (Array.isArray(internalValue) ? internalValue.map((ref) => {
@@ -65,7 +65,7 @@ export function ReferenceFilterField({
65
65
  }).filter(Boolean) as string[] : [internalValue.id])
66
66
  : [];
67
67
 
68
- function updateFilter(op: VirtualTableWhereFilterOp, val?: EntityReference | EntityReference[]) {
68
+ function updateFilter(op: VirtualTableWhereFilterOp, val?: EntityReference | EntityReference[] | null) {
69
69
 
70
70
  const prevOpIsArray = multipleSelectOperations.includes(operation);
71
71
  const newOpIsArray = multipleSelectOperations.includes(op);
@@ -142,7 +142,7 @@ export function ReferenceFilterField({
142
142
  return (
143
143
 
144
144
  <div className="flex w-[440px] flex-row">
145
- <div className="w-[120px]">
145
+ <div className="w-[140px]">
146
146
  <Select value={operation}
147
147
  onValueChange={(value) => {
148
148
  updateFilter(value as VirtualTableWhereFilterOp, internalValue);
@@ -156,21 +156,40 @@ export function ReferenceFilterField({
156
156
  </Select>
157
157
  </div>
158
158
 
159
- <div className="flex-grow ml-2 h-full">
159
+ <div className="flex-grow ml-2 h-full gap-2 flex flex-col">
160
160
 
161
161
  {internalValue && Array.isArray(internalValue) && <div>
162
162
  {internalValue.map((ref, index) => buildEntry(ref))}
163
163
  </div>}
164
+
164
165
  {internalValue && !Array.isArray(internalValue) && <div>
165
166
  {buildEntry(internalValue)}
166
167
  </div>}
168
+
167
169
  {(!internalValue || (Array.isArray(internalValue) && internalValue.length === 0)) &&
168
170
  <Button onClick={doOpenDialog}
169
171
  variant={"outlined"}
172
+ size={"large"}
170
173
  className="h-full w-full">
171
174
  {multiple ? "Select references" : "Select reference"}
172
175
  </Button>
173
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
+
174
193
  </div>
175
194
 
176
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>
@@ -20,7 +20,7 @@ import { VirtualTableContextProps } from "./types";
20
20
  import { VirtualTableHeaderRow } from "./VirtualTableHeaderRow";
21
21
  import { VirtualTableRow } from "./VirtualTableRow";
22
22
  import { VirtualTableCell } from "./VirtualTableCell";
23
- import { AssignmentIcon, cn, Markdown, Typography } from "@firecms/ui";
23
+ import { AssignmentIcon, CenteredView, cn, Typography } from "@firecms/ui";
24
24
 
25
25
  const VirtualListContext = createContext<VirtualTableContextProps<any>>({} as any);
26
26
  VirtualListContext.displayName = "VirtualListContext";
@@ -225,24 +225,7 @@ export const VirtualTable = React.memo<VirtualTableProps<any>>(
225
225
  }
226
226
 
227
227
  if (onFilterUpdate) onFilterUpdate(newFilterValue);
228
-
229
- if (column.key !== sortByProperty) {
230
- resetSort();
231
- }
232
- }, [checkFilterCombination, currentSort, onFilterUpdate, resetSort, sortByProperty]);
233
-
234
- const buildErrorView = useCallback(() => (
235
- <div
236
- className="h-full flex flex-col items-center justify-center sticky left-0">
237
-
238
- <Typography variant={"h6"}>
239
- {"Error fetching data from the data source"}
240
- </Typography>
241
-
242
- {error?.message && <Markdown className={"px-4 break-all"} source={error.message}/>}
243
-
244
- </div>
245
- ), [error?.message]);
228
+ }, [checkFilterCombination, currentSort, onFilterUpdate, sortByProperty]);
246
229
 
247
230
  const buildEmptyView = useCallback(() => {
248
231
  if (loading)
@@ -255,7 +238,18 @@ export const VirtualTable = React.memo<VirtualTableProps<any>>(
255
238
  }, [emptyComponent, loading]);
256
239
 
257
240
  const empty = !loading && (data?.length ?? 0) === 0;
258
- const customView = error ? buildErrorView() : (empty ? buildEmptyView() : undefined);
241
+ const customView = error
242
+ ? <CenteredView maxWidth={"2xl"}
243
+ className="flex flex-col gap-2">
244
+
245
+ <Typography variant={"h6"}>
246
+ {"Error fetching data from the data source"}
247
+ </Typography>
248
+
249
+ {error?.message && <SafeLinkRenderer text={error.message}/>}
250
+
251
+ </CenteredView>
252
+ : (empty ? buildEmptyView() : undefined);
259
253
 
260
254
  const virtualListController = {
261
255
  data,
@@ -403,3 +397,17 @@ function MemoizedList({
403
397
  {Row}
404
398
  </List>;
405
399
  }
400
+
401
+ const SafeLinkRenderer: React.FC<{
402
+ text: string;
403
+ }> = ({ text }) => {
404
+ const urlRegex = /https?:\/\/[^\s]+/g;
405
+ const htmlContent = text.replace(urlRegex, (url) => {
406
+ // For each URL found, replace it with an HTML <a> tag
407
+ return `<a href="${url}" target="_blank">Link</a><br/>`;
408
+ });
409
+
410
+ return (
411
+ <div className={"break-all"} dangerouslySetInnerHTML={{ __html: htmlContent }}/>
412
+ );
413
+ };
@@ -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"}
@@ -36,7 +36,7 @@ export interface OnCellValueChangeParams<T = any, M extends Record<string, any>
36
36
  propertyKey: string,
37
37
  entity: Entity<M>,
38
38
  onValueUpdated: () => void
39
- setError: (e: Error) => void
39
+ setError: (e: Error | undefined) => void
40
40
  fullPath: string
41
41
  context: FireCMSContext
42
42
  }
@@ -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>);
@@ -12,8 +12,7 @@ import { DataSourceContext } from "../contexts/DataSourceContext";
12
12
  import { SideEntityControllerContext } from "../contexts/SideEntityControllerContext";
13
13
  import { NavigationContext } from "../contexts/NavigationContext";
14
14
  import { SideDialogsControllerContext } from "../contexts/SideDialogsControllerContext";
15
- import { useLocaleConfig } from "../internal/useLocaleConfig";
16
- import { CenteredView, Typography } from "@firecms/ui";
15
+ import { CenteredView, Typography, useLocaleConfig } from "@firecms/ui";
17
16
  import { DialogsProvider } from "../contexts/DialogsProvider";
18
17
  import { useBuildDataSource } from "../internal/useBuildDataSource";
19
18
  import { useBuildCustomizationController } from "../internal/useBuildCustomizationController";
@@ -85,7 +84,7 @@ export function FireCMS<UserType extends User, EC extends EntityCollection>(prop
85
84
  onAnalyticsEvent
86
85
  }), []);
87
86
 
88
- const accessResponse = useProjectLog(authController);
87
+ const accessResponse = useProjectLog(authController, plugins);
89
88
 
90
89
  if (navigationController.navigationLoadingError) {
91
90
  return (
@@ -359,8 +359,7 @@ export function getDefaultFieldId(property: Property | ResolvedProperty) {
359
359
  } else if (property.dataType === "map") {
360
360
  if (property.keyValue)
361
361
  return "key_value";
362
- if (property.properties)
363
- return "group";
362
+ return "group";
364
363
  } else if (property.dataType === "array") {
365
364
  const of = (property as ArrayProperty).of;
366
365
  const oneOf = (property as ArrayProperty).oneOf;
@@ -443,7 +443,7 @@ function EntityFormInternal<M extends Record<string, any>>({
443
443
  pluginActions.push(...plugins.map((plugin, i) => (
444
444
  plugin.form?.Actions
445
445
  ? <plugin.form.Actions
446
- key={`actions_${plugin.name}`} {...actionProps}/>
446
+ key={`actions_${plugin.key}`} {...actionProps}/>
447
447
  : null
448
448
  )).filter(Boolean));
449
449
  }
@@ -43,7 +43,7 @@ export function DateTimeFieldBinding({
43
43
  <>
44
44
  <DateTimeField
45
45
  value={internalValue}
46
- onChange={(dateValue) => setValue(dateValue)}
46
+ onChange={(dateValue) => setValue(dateValue ?? null)}
47
47
  size={"medium"}
48
48
  mode={property.mode}
49
49
  clearable={property.clearable}
@@ -12,10 +12,10 @@ import {
12
12
  /**
13
13
  * @group Hooks and utilities
14
14
  */
15
- export type DeleteEntityWithCallbacksProps<M extends Record<string, any>> =
15
+ export type DeleteEntityWithCallbacksProps<M extends Record<string, any>, UserType extends User = User> =
16
16
  DeleteEntityProps<M>
17
17
  & {
18
- callbacks?: EntityCallbacks<M>;
18
+ callbacks?: EntityCallbacks<M, UserType>;
19
19
  onDeleteSuccess?: (entity: Entity<M>) => void;
20
20
  onDeleteFailure?: (entity: Entity<M>, e: Error) => void;
21
21
  onPreDeleteHookError?: (entity: Entity<M>, e: Error) => void;
@@ -62,7 +62,7 @@ export async function deleteEntityWithCallbacks<M extends Record<string, any>, U
62
62
 
63
63
  console.debug("Deleting entity", entity.path, entity.id);
64
64
 
65
- const entityDeleteProps: EntityOnDeleteProps<M, UserType> = {
65
+ const entityDeleteProps: EntityOnDeleteProps<M, any> = {
66
66
  entity,
67
67
  collection,
68
68
  entityId: entity.id,
@@ -64,7 +64,7 @@ export async function saveEntityWithCallbacks<M extends Record<string, any>, Use
64
64
  onPreSaveHookError,
65
65
  onSaveSuccessHookError
66
66
  }: SaveEntityWithCallbacksProps<M> & {
67
- collection: EntityCollection<M>,
67
+ collection: EntityCollection<M, UserType>,
68
68
  dataSource: DataSource,
69
69
  context: FireCMSContext<UserType>,
70
70
  }
@@ -145,6 +145,7 @@ export async function saveEntityWithCallbacks<M extends Record<string, any>, Use
145
145
  onSaveSuccess(entity);
146
146
  })
147
147
  .catch((e) => {
148
+ console.error("!!!", e);
148
149
  if (callbacks?.onSaveFailure) {
149
150
 
150
151
  const resolvedCollection = resolveCollection<M>({
@@ -6,7 +6,7 @@ import { DataSourceContext } from "../../contexts/DataSourceContext";
6
6
  * Use this hook to get the datasource being used
7
7
  * @group Hooks and utilities
8
8
  */
9
- export const useDataSource = (collection?: EntityCollection): DataSource => {
9
+ export const useDataSource = (collection?: EntityCollection<any, any>): DataSource => {
10
10
  const defaultDataSource = useContext(DataSourceContext);
11
11
  if (collection?.overrides?.dataSource)
12
12
  return collection?.overrides.dataSource;
@@ -7,10 +7,10 @@ import { useFireCMSContext } from "../useFireCMSContext";
7
7
  /**
8
8
  * @group Hooks and utilities
9
9
  */
10
- export interface EntityFetchProps<M extends Record<string, any>> {
10
+ export interface EntityFetchProps<M extends Record<string, any>, UserType extends User = User> {
11
11
  path: string;
12
12
  entityId?: string;
13
- collection: EntityCollection<M>;
13
+ collection: EntityCollection<M, UserType>;
14
14
  useCache?: boolean;
15
15
  }
16
16
 
@@ -41,7 +41,7 @@ export function useEntityFetch<M extends Record<string, any>, UserType extends U
41
41
  entityId,
42
42
  collection,
43
43
  useCache = false
44
- }: EntityFetchProps<M>): EntityFetchResult<M> {
44
+ }: EntityFetchProps<M, UserType>): EntityFetchResult<M> {
45
45
 
46
46
  const dataSource = useDataSource(collection);
47
47
  const navigationController = useNavigationController();
@@ -25,3 +25,5 @@ export * from "./useBuildNavigationController";
25
25
 
26
26
  export * from "./useBuildLocalConfigurationPersistence";
27
27
  export * from "./useBuildModeController";
28
+
29
+ export * from "./useValidateAuthenticator";
@@ -1,10 +1,10 @@
1
- import { useCallback, useEffect, useState } from "react";
1
+ import { useCallback, useEffect, useRef, useState } from "react";
2
2
  import { PartialEntityCollection, UserConfigurationPersistence } from "../types";
3
3
  import { mergeDeep, stripCollectionPath } from "../util";
4
4
 
5
5
  export function useBuildLocalConfigurationPersistence(): UserConfigurationPersistence {
6
6
 
7
- const [configCache, setConfigCache] = useState<Record<string, PartialEntityCollection>>({});
7
+ const configCache = useRef<Record<string, PartialEntityCollection>>({});
8
8
 
9
9
  const getCollectionFromStorage = useCallback((storageKey: string) => {
10
10
  const item = localStorage.getItem(storageKey);
@@ -13,20 +13,19 @@ export function useBuildLocalConfigurationPersistence(): UserConfigurationPersis
13
13
 
14
14
  const getCollectionConfig = useCallback(<M extends Record<string, any>>(path: string): PartialEntityCollection<M> => {
15
15
  const storageKey = `collection_config::${stripCollectionPath(path)}`;
16
- if (configCache[storageKey]) {
17
- return configCache[storageKey] as PartialEntityCollection<M>;
16
+ if (configCache.current[storageKey]) {
17
+ return configCache.current[storageKey] as PartialEntityCollection<M>;
18
18
  }
19
19
  return getCollectionFromStorage(storageKey);
20
- }, [configCache, getCollectionFromStorage]);
20
+ }, [getCollectionFromStorage]);
21
21
 
22
22
  const onCollectionModified = useCallback(<M extends Record<string, any>>(path: string, data: PartialEntityCollection<M>) => {
23
23
  const storageKey = `collection_config::${stripCollectionPath(path)}`;
24
24
  localStorage.setItem(storageKey, JSON.stringify(data));
25
- setConfigCache((currentCache) => {
26
- const cachedConfig = currentCache[storageKey];
27
- const newConfig = mergeDeep(cachedConfig ?? getCollectionFromStorage(path), data);
28
- return (mergeDeep(currentCache, newConfig));
29
- });
25
+ const currentCache = configCache.current;
26
+ const cachedConfig = currentCache[storageKey];
27
+ const newConfig = mergeDeep(cachedConfig ?? getCollectionFromStorage(path), data);
28
+ configCache.current = mergeDeep(currentCache, newConfig);
30
29
  }, [getCollectionFromStorage]);
31
30
 
32
31
  const [recentlyVisitedPaths, _setRecentlyVisitedPaths] = useState<string[]>([]);
@@ -8,8 +8,13 @@ import { ModeController } from "./index";
8
8
  */
9
9
  export function useBuildModeController(): ModeController {
10
10
 
11
- const prefersDarkModeQuery = typeof window !== "undefined" &&
12
- window.matchMedia("(prefers-color-scheme: dark)");
11
+ const prefersDarkModeQuery = useCallback((): boolean => {
12
+ if (typeof window === "undefined")
13
+ return false;
14
+ const mediaQueryList = window.matchMedia("(prefers-color-scheme: dark)");
15
+ return mediaQueryList.matches;
16
+ }, []);
17
+
13
18
  const prefersDarkModeStorage: boolean | null = localStorage.getItem("prefers-dark-mode") != null ? localStorage.getItem("prefers-dark-mode") === "true" : null;
14
19
  const prefersDarkMode = prefersDarkModeStorage ?? prefersDarkModeQuery;
15
20
  const [mode, setMode] = useState<"light" | "dark">(prefersDarkMode ? "dark" : "light");
@@ -23,7 +28,7 @@ export function useBuildModeController(): ModeController {
23
28
  const setDarkMode = useCallback(() => {
24
29
  setMode("dark");
25
30
  setDocumentMode("dark");
26
- }, [prefersDarkModeQuery]);
31
+ }, []);
27
32
 
28
33
  const setLightMode = useCallback(() => {
29
34
  setMode("light");
@@ -37,14 +42,15 @@ export function useBuildModeController(): ModeController {
37
42
 
38
43
  const toggleMode = useCallback(() => {
39
44
 
45
+ const prefersDarkModeQueryResult = prefersDarkModeQuery();
40
46
  if (mode === "light") {
41
- if (!prefersDarkModeQuery)
47
+ if (!prefersDarkModeQueryResult)
42
48
  localStorage.setItem("prefers-dark-mode", "true");
43
49
  else
44
50
  localStorage.removeItem("prefers-dark-mode");
45
51
  setDarkMode();
46
52
  } else {
47
- if (prefersDarkModeQuery)
53
+ if (prefersDarkModeQueryResult)
48
54
  localStorage.setItem("prefers-dark-mode", "false");
49
55
  else
50
56
  localStorage.removeItem("prefers-dark-mode");