@firecms/core 3.0.0-beta.5 → 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.
- package/dist/components/ClearFilterSortButton.d.ts +5 -0
- package/dist/components/EntityCollectionTable/EntityCollectionRowActions.d.ts +11 -11
- package/dist/components/EntityCollectionTable/EntityCollectionTableProps.d.ts +1 -1
- package/dist/components/EntityCollectionTable/internal/CollectionTableToolbar.d.ts +1 -4
- package/dist/components/EntityCollectionView/EntityCollectionView.d.ts +11 -1
- package/dist/components/EntityCollectionView/EntityCollectionViewStartActions.d.ts +11 -0
- package/dist/components/SelectableTable/SelectableTable.d.ts +1 -1
- package/dist/components/common/types.d.ts +1 -1
- package/dist/form/components/ErrorFocus.d.ts +1 -1
- package/dist/hooks/useValidateAuthenticator.d.ts +2 -6
- package/dist/index.es.js +3516 -3381
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +5 -5
- package/dist/index.umd.js.map +1 -1
- package/dist/internal/useBuildDataSource.d.ts +1 -16
- package/dist/types/collections.d.ts +5 -1
- package/dist/types/datasource.d.ts +2 -5
- package/dist/types/entities.d.ts +5 -1
- package/dist/types/index.d.ts +0 -1
- package/dist/types/navigation.d.ts +1 -1
- package/dist/types/plugins.d.ts +3 -1
- package/dist/types/properties.d.ts +2 -2
- package/dist/util/entities.d.ts +1 -1
- package/dist/util/navigation_utils.d.ts +2 -2
- package/dist/util/resolutions.d.ts +5 -5
- package/package.json +22 -19
- package/src/components/ClearFilterSortButton.tsx +41 -0
- package/src/components/EntityCollectionTable/EntityCollectionTable.tsx +10 -11
- package/src/components/EntityCollectionTable/EntityCollectionTableProps.tsx +1 -1
- package/src/components/EntityCollectionTable/PropertyTableCell.tsx +16 -19
- package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +27 -32
- package/src/components/EntityCollectionTable/internal/EntityTableCell.tsx +11 -6
- package/src/components/EntityCollectionView/EntityCollectionView.tsx +28 -5
- package/src/components/EntityCollectionView/EntityCollectionViewStartActions.tsx +68 -0
- package/src/components/EntityView.tsx +1 -1
- package/src/components/HomePage/DefaultHomePage.tsx +1 -1
- package/src/components/SelectableTable/SelectableTable.tsx +1 -1
- package/src/components/SelectableTable/filters/BooleanFilterField.tsx +2 -3
- package/src/components/SelectableTable/filters/DateTimeFilterField.tsx +22 -7
- package/src/components/SelectableTable/filters/ReferenceFilterField.tsx +24 -5
- package/src/components/SelectableTable/filters/StringNumberFilterField.tsx +35 -15
- package/src/components/VirtualTable/VirtualTable.tsx +28 -20
- package/src/components/common/types.tsx +1 -1
- package/src/core/FireCMS.tsx +1 -2
- package/src/core/field_configs.tsx +1 -2
- package/src/hooks/data/save.ts +1 -0
- package/src/hooks/useBuildLocalConfigurationPersistence.tsx +9 -10
- package/src/hooks/useBuildModeController.tsx +11 -5
- package/src/hooks/useBuildNavigationController.tsx +76 -19
- package/src/hooks/useProjectLog.tsx +3 -3
- package/src/hooks/useValidateAuthenticator.tsx +2 -22
- package/src/internal/useBuildDataSource.ts +42 -47
- package/src/internal/useBuildSideEntityController.tsx +16 -13
- package/src/preview/PropertyPreview.tsx +2 -12
- package/src/types/collections.ts +5 -1
- package/src/types/datasource.ts +7 -4
- package/src/types/entities.ts +9 -1
- package/src/types/index.ts +0 -1
- package/src/types/navigation.ts +1 -1
- package/src/types/plugins.tsx +4 -3
- package/src/types/properties.ts +5 -4
- package/src/util/entities.ts +1 -1
- package/src/util/navigation_utils.ts +6 -6
- package/dist/internal/useLocaleConfig.d.ts +0 -1
- package/dist/types/appcheck.d.ts +0 -26
- package/src/internal/useLocaleConfig.tsx +0 -18
- package/src/types/appcheck.ts +0 -29
|
@@ -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
|
|
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
|
-
}, [
|
|
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
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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 =
|
|
12
|
-
|
|
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
|
-
}, [
|
|
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 (!
|
|
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 (
|
|
53
|
+
if (prefersDarkModeQueryResult)
|
|
48
54
|
localStorage.setItem("prefers-dark-mode", "false");
|
|
49
55
|
else
|
|
50
56
|
localStorage.removeItem("prefers-dark-mode");
|
|
@@ -130,6 +130,30 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
|
|
|
130
130
|
.filter(Boolean) as TopNavigationEntry[]
|
|
131
131
|
];
|
|
132
132
|
|
|
133
|
+
// Sort by group, entries with group "Admin" will go last, and second to last will be the group "Views"
|
|
134
|
+
navigationEntries = navigationEntries.sort((a, b) => {
|
|
135
|
+
if (a.group !== "Views" && a.group !== "Admin" && (b.group === "Views" || b.group === "Admin")) {
|
|
136
|
+
return -1;
|
|
137
|
+
}
|
|
138
|
+
if (b.group !== "Views" && b.group !== "Admin" && (a.group === "Views" || a.group === "Admin")) {
|
|
139
|
+
return 1;
|
|
140
|
+
}
|
|
141
|
+
if (a.group === "Admin" && b.group !== "Admin") {
|
|
142
|
+
return 1;
|
|
143
|
+
}
|
|
144
|
+
if (a.group !== "Admin" && b.group === "Admin") {
|
|
145
|
+
return -1;
|
|
146
|
+
}
|
|
147
|
+
if (a.group === "Views" && b.group !== "Views") {
|
|
148
|
+
return -1;
|
|
149
|
+
}
|
|
150
|
+
if (a.group !== "Views" && b.group === "Views") {
|
|
151
|
+
return 1;
|
|
152
|
+
}
|
|
153
|
+
return 0;
|
|
154
|
+
|
|
155
|
+
});
|
|
156
|
+
|
|
133
157
|
if (viewsOrder) {
|
|
134
158
|
navigationEntries = navigationEntries.sort((a, b) => {
|
|
135
159
|
const aIndex = viewsOrder.indexOf(a.path);
|
|
@@ -163,6 +187,8 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
|
|
|
163
187
|
if (authController.initialLoading)
|
|
164
188
|
return;
|
|
165
189
|
|
|
190
|
+
console.debug("Refreshing navigation");
|
|
191
|
+
|
|
166
192
|
try {
|
|
167
193
|
|
|
168
194
|
const [resolvedCollections = [], resolvedViews, resolvedAdminViews = []] = await Promise.all([
|
|
@@ -172,16 +198,23 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
|
|
|
172
198
|
]
|
|
173
199
|
);
|
|
174
200
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
!equal(viewsRef.current, resolvedViews) ||
|
|
178
|
-
!equal(adminViewsRef.current, resolvedAdminViews) ||
|
|
179
|
-
!equal(topLevelNavigation, computeTopNavigation(resolvedCollections, resolvedViews, resolvedAdminViews, viewsOrder))
|
|
180
|
-
) {
|
|
201
|
+
let shouldUpdateTopLevelNav = false;
|
|
202
|
+
if (!areCollectionListsEqual(collectionsRef.current ?? [], resolvedCollections)) {
|
|
181
203
|
collectionsRef.current = resolvedCollections;
|
|
204
|
+
shouldUpdateTopLevelNav = true;
|
|
205
|
+
}
|
|
206
|
+
if (!equal(viewsRef.current, resolvedViews)) {
|
|
182
207
|
viewsRef.current = resolvedViews;
|
|
208
|
+
shouldUpdateTopLevelNav = true;
|
|
209
|
+
}
|
|
210
|
+
if (!equal(adminViewsRef.current, resolvedAdminViews)) {
|
|
183
211
|
adminViewsRef.current = resolvedAdminViews;
|
|
184
|
-
|
|
212
|
+
shouldUpdateTopLevelNav = true;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const computedTopLevelNav = computeTopNavigation(resolvedCollections, resolvedViews, resolvedAdminViews, viewsOrder);
|
|
216
|
+
if (shouldUpdateTopLevelNav && !equal(topLevelNavigation, computedTopLevelNav)) {
|
|
217
|
+
setTopLevelNavigation(computedTopLevelNav);
|
|
185
218
|
}
|
|
186
219
|
} catch (e) {
|
|
187
220
|
console.error(e);
|
|
@@ -243,9 +276,9 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
|
|
|
243
276
|
const getCollectionFromPaths = useCallback(<EC extends EntityCollection>(pathSegments: string[]): EC | undefined => {
|
|
244
277
|
|
|
245
278
|
const collections = collectionsRef.current;
|
|
279
|
+
if (collections === undefined)
|
|
280
|
+
throw Error("getCollectionFromPaths: Collections have not been initialised yet");
|
|
246
281
|
let currentCollections: EntityCollection[] | undefined = [...(collections ?? [])];
|
|
247
|
-
if (!currentCollections)
|
|
248
|
-
throw Error("Collections have not been initialised yet");
|
|
249
282
|
|
|
250
283
|
for (let i = 0; i < pathSegments.length; i++) {
|
|
251
284
|
const pathSegment = pathSegments[i];
|
|
@@ -264,9 +297,9 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
|
|
|
264
297
|
const getCollectionFromIds = useCallback(<EC extends EntityCollection>(ids: string[]): EC | undefined => {
|
|
265
298
|
|
|
266
299
|
const collections = collectionsRef.current;
|
|
300
|
+
if (collections === undefined)
|
|
301
|
+
throw Error("getCollectionFromIds: Collections have not been initialised yet");
|
|
267
302
|
let currentCollections: EntityCollection[] | undefined = [...(collections ?? [])];
|
|
268
|
-
if (!currentCollections)
|
|
269
|
-
throw Error("Collections have not been initialised yet");
|
|
270
303
|
|
|
271
304
|
for (let i = 0; i < ids.length; i++) {
|
|
272
305
|
const id = ids[i];
|
|
@@ -302,9 +335,7 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
|
|
|
302
335
|
[]);
|
|
303
336
|
|
|
304
337
|
const resolveAliasesFrom = useCallback((path: string): string => {
|
|
305
|
-
const collections = collectionsRef.current;
|
|
306
|
-
if (!collections)
|
|
307
|
-
throw Error("Collections have not been initialised yet");
|
|
338
|
+
const collections = collectionsRef.current ?? [];
|
|
308
339
|
return resolveCollectionPathIds(path, collections);
|
|
309
340
|
}, []);
|
|
310
341
|
|
|
@@ -407,7 +438,7 @@ async function resolveCollections(collections: undefined | EntityCollection[] |
|
|
|
407
438
|
collectionPermissions: PermissionsBuilder | undefined,
|
|
408
439
|
authController: AuthController,
|
|
409
440
|
dataSource: DataSourceDelegate,
|
|
410
|
-
injectCollections?: (collections: EntityCollection[]) => EntityCollection[]) {
|
|
441
|
+
injectCollections?: (collections: EntityCollection[]) => EntityCollection[]): Promise<EntityCollection[]> {
|
|
411
442
|
let resolvedCollections: EntityCollection[] = [];
|
|
412
443
|
if (typeof collections === "function") {
|
|
413
444
|
resolvedCollections = await collections({
|
|
@@ -419,14 +450,14 @@ async function resolveCollections(collections: undefined | EntityCollection[] |
|
|
|
419
450
|
resolvedCollections = collections;
|
|
420
451
|
}
|
|
421
452
|
|
|
422
|
-
resolvedCollections = applyPermissionsFunctionIfEmpty(resolvedCollections, collectionPermissions);
|
|
423
|
-
|
|
424
|
-
resolvedCollections = filterOutNotAllowedCollections(resolvedCollections, authController);
|
|
425
|
-
|
|
426
453
|
if (injectCollections) {
|
|
427
454
|
resolvedCollections = injectCollections(resolvedCollections ?? []);
|
|
428
455
|
}
|
|
429
456
|
|
|
457
|
+
resolvedCollections = applyPermissionsFunctionIfEmpty(resolvedCollections, collectionPermissions);
|
|
458
|
+
|
|
459
|
+
resolvedCollections = filterOutNotAllowedCollections(resolvedCollections, authController);
|
|
460
|
+
|
|
430
461
|
return resolvedCollections;
|
|
431
462
|
}
|
|
432
463
|
|
|
@@ -451,3 +482,29 @@ function getGroup(collectionOrView: EntityCollection<any, any> | CMSView) {
|
|
|
451
482
|
}
|
|
452
483
|
return trimmed ?? "Views";
|
|
453
484
|
}
|
|
485
|
+
|
|
486
|
+
function areCollectionListsEqual(a: EntityCollection[], b: EntityCollection[]) {
|
|
487
|
+
if (a.length !== b.length) {
|
|
488
|
+
return false;
|
|
489
|
+
}
|
|
490
|
+
const aCopy = [...a];
|
|
491
|
+
const bCopy = [...b];
|
|
492
|
+
const aSorted = aCopy.sort((x, y) => x.id.localeCompare(y.id));
|
|
493
|
+
const bSorted = bCopy.sort((x, y) => x.id.localeCompare(y.id));
|
|
494
|
+
return aSorted.every((value, index) => areCollectionsEqual(value, bSorted[index]));
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
function areCollectionsEqual(a: EntityCollection, b: EntityCollection) {
|
|
498
|
+
const {
|
|
499
|
+
subcollections: subcollectionsA,
|
|
500
|
+
...restA
|
|
501
|
+
} = a;
|
|
502
|
+
const {
|
|
503
|
+
subcollections: subcollectionsB,
|
|
504
|
+
...restB
|
|
505
|
+
} = b;
|
|
506
|
+
if (!areCollectionListsEqual(subcollectionsA ?? [], subcollectionsB ?? [])) {
|
|
507
|
+
return false;
|
|
508
|
+
}
|
|
509
|
+
return equal(restA, restB);
|
|
510
|
+
}
|
|
@@ -9,7 +9,7 @@ export type AccessResponse = {
|
|
|
9
9
|
message?: string;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
async function makeRequest(authController: AuthController, pluginKeys: string | undefined) {
|
|
12
|
+
async function makeRequest(authController: AuthController, pluginKeys: string[] | undefined) {
|
|
13
13
|
const firebaseToken = await authController.getAuthToken();
|
|
14
14
|
return fetch(DEFAULT_SERVER + "/access_log",
|
|
15
15
|
{
|
|
@@ -30,12 +30,12 @@ export function useProjectLog(authController: AuthController,
|
|
|
30
30
|
plugins?: FireCMSPlugin<any, any, any>[]): AccessResponse | null {
|
|
31
31
|
const [accessResponse, setAccessResponse] = useState<AccessResponse | null>(null);
|
|
32
32
|
const accessedUserRef = useRef<string | null>(null);
|
|
33
|
-
const pluginKeys = plugins?.map(plugin => plugin.key)
|
|
33
|
+
const pluginKeys = plugins?.map(plugin => plugin.key);
|
|
34
34
|
useEffect(() => {
|
|
35
35
|
if (authController.user && authController.user.uid !== accessedUserRef.current && !authController.initialLoading) {
|
|
36
36
|
makeRequest(authController, pluginKeys).then(setAccessResponse);
|
|
37
37
|
accessedUserRef.current = authController.user.uid;
|
|
38
38
|
}
|
|
39
|
-
}, [authController]);
|
|
39
|
+
}, [authController, pluginKeys]);
|
|
40
40
|
return accessResponse;
|
|
41
41
|
}
|
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
2
2
|
import equal from "react-fast-compare";
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { AuthController, Authenticator, DataSourceDelegate, StorageSource, User } from "../index";
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* This hook is used internally for validating an authenticator.
|
|
8
8
|
*
|
|
9
9
|
* @param authController
|
|
10
10
|
* @param authentication
|
|
11
|
-
* @param getAppCheckToken
|
|
12
|
-
* @param appCheckForceRefresh
|
|
13
11
|
* @param storageSource
|
|
14
12
|
* @param dataSourceDelegate
|
|
15
13
|
*/
|
|
@@ -17,8 +15,6 @@ export function useValidateAuthenticator<UserType extends User = User, Controlle
|
|
|
17
15
|
disabled,
|
|
18
16
|
authController,
|
|
19
17
|
authenticator,
|
|
20
|
-
getAppCheckToken,
|
|
21
|
-
appCheckForceRefresh = false,
|
|
22
18
|
storageSource,
|
|
23
19
|
dataSourceDelegate
|
|
24
20
|
}:
|
|
@@ -26,8 +22,6 @@ export function useValidateAuthenticator<UserType extends User = User, Controlle
|
|
|
26
22
|
disabled?: boolean,
|
|
27
23
|
authController: Controller,
|
|
28
24
|
authenticator?: boolean | Authenticator<UserType, Controller>,
|
|
29
|
-
getAppCheckToken?: (forceRefresh: boolean) => Promise<AppCheckTokenResult> | undefined,
|
|
30
|
-
appCheckForceRefresh?: boolean,
|
|
31
25
|
dataSourceDelegate: DataSourceDelegate;
|
|
32
26
|
storageSource: StorageSource;
|
|
33
27
|
}): {
|
|
@@ -78,20 +72,6 @@ export function useValidateAuthenticator<UserType extends User = User, Controlle
|
|
|
78
72
|
const delegateUser = authController.user;
|
|
79
73
|
console.debug("Checking authentication for user", delegateUser);
|
|
80
74
|
|
|
81
|
-
if (getAppCheckToken) {
|
|
82
|
-
try {
|
|
83
|
-
if (!await getAppCheckToken(appCheckForceRefresh)) {
|
|
84
|
-
setNotAllowedError("App Check failed.");
|
|
85
|
-
authController.signOut();
|
|
86
|
-
} else {
|
|
87
|
-
console.debug("App Check success.");
|
|
88
|
-
}
|
|
89
|
-
} catch (e: any) {
|
|
90
|
-
setNotAllowedError(e.message);
|
|
91
|
-
authController.signOut();
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
75
|
if (authenticator instanceof Function && delegateUser && !equal(checkedUserRef.current?.uid, delegateUser.uid)) {
|
|
96
76
|
setAuthLoading(true);
|
|
97
77
|
try {
|
|
@@ -120,7 +100,7 @@ export function useValidateAuthenticator<UserType extends User = User, Controlle
|
|
|
120
100
|
setAuthVerified(true);
|
|
121
101
|
}
|
|
122
102
|
|
|
123
|
-
}, [disabled, authController, authenticator,
|
|
103
|
+
}, [disabled, authController, authenticator, dataSourceDelegate, storageSource]);
|
|
124
104
|
|
|
125
105
|
useEffect(() => {
|
|
126
106
|
checkAuthentication();
|
|
@@ -5,12 +5,10 @@ import {
|
|
|
5
5
|
DeleteEntityProps,
|
|
6
6
|
Entity,
|
|
7
7
|
EntityCollection,
|
|
8
|
-
EntityReference,
|
|
9
8
|
EntityValues,
|
|
10
9
|
FetchCollectionProps,
|
|
11
10
|
FetchEntityProps,
|
|
12
11
|
FilterValues,
|
|
13
|
-
GeoPoint,
|
|
14
12
|
ListenCollectionProps,
|
|
15
13
|
ListenEntityProps,
|
|
16
14
|
NavigationController,
|
|
@@ -207,13 +205,10 @@ export function useBuildDataSource({
|
|
|
207
205
|
|
|
208
206
|
const properties: ResolvedProperties<M> | undefined = resolvedCollection?.properties;
|
|
209
207
|
|
|
210
|
-
const firestoreValues = cmsToDelegateModel(
|
|
208
|
+
const firestoreValues = delegate.cmsToDelegateModel(
|
|
211
209
|
values,
|
|
212
|
-
delegate.buildReference,
|
|
213
|
-
delegate.buildGeoPoint,
|
|
214
|
-
delegate.buildDate,
|
|
215
|
-
delegate.buildDeleteFieldValue
|
|
216
210
|
);
|
|
211
|
+
|
|
217
212
|
const updatedFirestoreValues: EntityValues<M> = properties
|
|
218
213
|
? updateDateAutoValues(
|
|
219
214
|
{
|
|
@@ -316,48 +311,48 @@ export function useBuildDataSource({
|
|
|
316
311
|
sortBy
|
|
317
312
|
}
|
|
318
313
|
)
|
|
319
|
-
}, [delegate.isFilterCombinationValid])
|
|
314
|
+
}, [delegate.isFilterCombinationValid]),
|
|
320
315
|
|
|
321
316
|
};
|
|
322
317
|
|
|
323
318
|
}
|
|
324
319
|
|
|
325
|
-
/**
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
export function cmsToDelegateModel(data: any,
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
): any {
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
}
|
|
320
|
+
// /**
|
|
321
|
+
// * Recursive function that converts Firestore data types into CMS or plain
|
|
322
|
+
// * JS types.
|
|
323
|
+
// * FireCMS uses Javascript dates internally instead of Firestore timestamps.
|
|
324
|
+
// * This makes it easier to interact with the rest of the libraries and
|
|
325
|
+
// * bindings.
|
|
326
|
+
// * Also, Firestore references are replaced with {@link EntityReference}
|
|
327
|
+
// * @param data
|
|
328
|
+
// * @param buildReference
|
|
329
|
+
// * @param buildGeoPoint
|
|
330
|
+
// * @param buildDate
|
|
331
|
+
// * @param buildDelete
|
|
332
|
+
// * @group Firestore
|
|
333
|
+
// */
|
|
334
|
+
// export function cmsToDelegateModel(data: any,
|
|
335
|
+
// buildReference: (reference: EntityReference) => any,
|
|
336
|
+
// buildGeoPoint: (geoPoint: GeoPoint) => any,
|
|
337
|
+
// buildDate: (date: Date) => any,
|
|
338
|
+
// buildDelete: () => any
|
|
339
|
+
// ): any {
|
|
340
|
+
// if (data === undefined) {
|
|
341
|
+
// return buildDelete();
|
|
342
|
+
// } else if (data === null) {
|
|
343
|
+
// return null;
|
|
344
|
+
// } else if (Array.isArray(data)) {
|
|
345
|
+
// return data.map(v => cmsToDelegateModel(v, buildReference, buildGeoPoint, buildDate, buildDelete));
|
|
346
|
+
// } else if (data.isEntityReference && data.isEntityReference()) {
|
|
347
|
+
// return buildReference(data);
|
|
348
|
+
// } else if (data instanceof GeoPoint) {
|
|
349
|
+
// return buildGeoPoint(data);
|
|
350
|
+
// } else if (data instanceof Date) {
|
|
351
|
+
// return buildDate(data);
|
|
352
|
+
// } else if (data && typeof data === "object") {
|
|
353
|
+
// return Object.entries(data)
|
|
354
|
+
// .map(([key, v]) => ({ [key]: cmsToDelegateModel(v, buildReference, buildGeoPoint, buildDate, buildDelete) }))
|
|
355
|
+
// .reduce((a, b) => ({ ...a, ...b }), {});
|
|
356
|
+
// }
|
|
357
|
+
// return data;
|
|
358
|
+
// }
|
|
@@ -43,17 +43,17 @@ export const useBuildSideEntityController = (navigation: NavigationController,
|
|
|
43
43
|
const panel = panelsFromUrl[i];
|
|
44
44
|
setTimeout(() => {
|
|
45
45
|
if (i === 0)
|
|
46
|
-
sideDialogsController.replace(propsToSidePanel(panel, navigation, smallLayout));
|
|
46
|
+
sideDialogsController.replace(propsToSidePanel(panel, navigation.buildUrlCollectionPath, navigation.resolveAliasesFrom, smallLayout));
|
|
47
47
|
else
|
|
48
|
-
sideDialogsController.open(propsToSidePanel(panel, navigation, smallLayout))
|
|
48
|
+
sideDialogsController.open(propsToSidePanel(panel, navigation.buildUrlCollectionPath, navigation.resolveAliasesFrom, smallLayout))
|
|
49
49
|
}, 1);
|
|
50
50
|
}
|
|
51
51
|
} else {
|
|
52
|
-
console.warn("Location path is not a collection path");
|
|
52
|
+
// console.warn("Location path is not a collection path");
|
|
53
53
|
}
|
|
54
54
|
initialised.current = true;
|
|
55
55
|
}
|
|
56
|
-
}, [location, navigation, sideDialogsController, smallLayout]);
|
|
56
|
+
}, [location, navigation.loading, navigation.isUrlCollectionPath, navigation.buildUrlCollectionPath, navigation.resolveAliasesFrom, sideDialogsController, smallLayout, navigation]);
|
|
57
57
|
|
|
58
58
|
const close = useCallback(() => {
|
|
59
59
|
sideDialogsController.close();
|
|
@@ -76,9 +76,9 @@ export const useBuildSideEntityController = (navigation: NavigationController,
|
|
|
76
76
|
sideDialogsController.open(propsToSidePanel({
|
|
77
77
|
selectedSubPath: defaultSelectedView,
|
|
78
78
|
...props,
|
|
79
|
-
}, navigation, smallLayout));
|
|
79
|
+
}, navigation.buildUrlCollectionPath, navigation.resolveAliasesFrom, smallLayout));
|
|
80
80
|
|
|
81
|
-
}, [sideDialogsController, navigation, smallLayout]);
|
|
81
|
+
}, [sideDialogsController, navigation.buildUrlCollectionPath, navigation.resolveAliasesFrom, smallLayout]);
|
|
82
82
|
|
|
83
83
|
const replace = useCallback((props: EntitySidePanelProps<any>) => {
|
|
84
84
|
|
|
@@ -86,9 +86,9 @@ export const useBuildSideEntityController = (navigation: NavigationController,
|
|
|
86
86
|
throw Error("If you want to copy an entity you need to provide an entityId");
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
sideDialogsController.replace(propsToSidePanel(props, navigation, smallLayout));
|
|
89
|
+
sideDialogsController.replace(propsToSidePanel(props, navigation.buildUrlCollectionPath, navigation.resolveAliasesFrom, smallLayout));
|
|
90
90
|
|
|
91
|
-
}, [navigation, sideDialogsController, smallLayout]);
|
|
91
|
+
}, [navigation.buildUrlCollectionPath, navigation.resolveAliasesFrom, sideDialogsController, smallLayout]);
|
|
92
92
|
|
|
93
93
|
return {
|
|
94
94
|
close,
|
|
@@ -149,14 +149,17 @@ export function buildSidePanelsFromUrl(path: string, collections: EntityCollecti
|
|
|
149
149
|
return sidePanels;
|
|
150
150
|
}
|
|
151
151
|
|
|
152
|
-
const propsToSidePanel = (props: EntitySidePanelProps<any>,
|
|
152
|
+
const propsToSidePanel = (props: EntitySidePanelProps<any>,
|
|
153
|
+
buildUrlCollectionPath: (path: string) => string,
|
|
154
|
+
resolveAliasesFrom: (pathWithAliases: string) => string,
|
|
155
|
+
smallLayout: boolean): SideDialogPanelProps => {
|
|
153
156
|
|
|
154
157
|
const collectionPath = removeInitialAndTrailingSlashes(props.path);
|
|
155
158
|
|
|
156
159
|
const newPath = props.entityId
|
|
157
|
-
?
|
|
158
|
-
:
|
|
159
|
-
const resolvedPath =
|
|
160
|
+
? buildUrlCollectionPath(`${collectionPath}/${props.entityId}/${props.selectedSubPath || ""}`)
|
|
161
|
+
: buildUrlCollectionPath(`${collectionPath}#${NEW_URL_HASH}`);
|
|
162
|
+
const resolvedPath = resolveAliasesFrom(props.path);
|
|
160
163
|
|
|
161
164
|
const resolvedPanelProps: EntitySidePanelProps<any> = {
|
|
162
165
|
...props,
|
|
@@ -167,7 +170,7 @@ const propsToSidePanel = (props: EntitySidePanelProps<any>, navigation: Navigati
|
|
|
167
170
|
key: `${props.path}/${props.entityId}`,
|
|
168
171
|
component: <EntitySidePanel {...resolvedPanelProps}/>,
|
|
169
172
|
urlPath: newPath,
|
|
170
|
-
parentUrlPath:
|
|
173
|
+
parentUrlPath: buildUrlCollectionPath(collectionPath),
|
|
171
174
|
width: getEntityViewWidth(props, smallLayout),
|
|
172
175
|
onClose: props.onClose
|
|
173
176
|
});
|
|
@@ -92,7 +92,7 @@ export const PropertyPreview = React.memo(function PropertyPreview<T extends CMS
|
|
|
92
92
|
size={props.size}
|
|
93
93
|
storagePathOrDownloadUrl={value}/>;
|
|
94
94
|
} else if (stringProperty.markdown) {
|
|
95
|
-
content = <Markdown source={value}/>;
|
|
95
|
+
content = <Markdown source={value} size={"small"}/>;
|
|
96
96
|
} else {
|
|
97
97
|
content = <StringPropertyPreview {...props}
|
|
98
98
|
property={stringProperty}
|
|
@@ -113,17 +113,7 @@ export const PropertyPreview = React.memo(function PropertyPreview<T extends CMS
|
|
|
113
113
|
content = <ArrayPropertyPreview {...props}
|
|
114
114
|
value={value}
|
|
115
115
|
property={property as ResolvedArrayProperty}/>;
|
|
116
|
-
}
|
|
117
|
-
// else if (arrayProperty.of.dataType === "map") {
|
|
118
|
-
// content =
|
|
119
|
-
// <ArrayOfMapsPreview propertyKey={propertyKey}
|
|
120
|
-
// property={property as ResolvedArrayProperty}
|
|
121
|
-
// value={value as Record<string, any>[]} // This might be wrong
|
|
122
|
-
// entity={entity}
|
|
123
|
-
// size={size}
|
|
124
|
-
// />;
|
|
125
|
-
// }
|
|
126
|
-
else if (arrayProperty.of.dataType === "reference") {
|
|
116
|
+
} else if (arrayProperty.of.dataType === "reference") {
|
|
127
117
|
content = <ArrayOfReferencesPreview {...props}
|
|
128
118
|
value={value}
|
|
129
119
|
property={property as ResolvedArrayProperty}/>;
|
package/src/types/collections.ts
CHANGED
|
@@ -107,6 +107,10 @@ export interface EntityCollection<M extends Record<string, any> = any, UserType
|
|
|
107
107
|
* `subcollection:`. e.g. `subcollection:orders`.
|
|
108
108
|
* - If you are using a collection group, you will also have an
|
|
109
109
|
* additional `collectionGroupParent` column.
|
|
110
|
+
* You can use this prop to hide some properties from the table view.
|
|
111
|
+
* Note that if you set this prop, other ways to hide fields, like
|
|
112
|
+
* `hidden` in the property definition,will be ignored.
|
|
113
|
+
* `propertiesOrder` has precedence over `hidden`.
|
|
110
114
|
*/
|
|
111
115
|
propertiesOrder?: Extract<keyof M, string>[];
|
|
112
116
|
|
|
@@ -523,7 +527,7 @@ export type EntityTableController<M extends Record<string, any> = any> = {
|
|
|
523
527
|
filterValues?: FilterValues<Extract<keyof M, string>>;
|
|
524
528
|
setFilterValues?: (filterValues: FilterValues<Extract<keyof M, string>>) => void;
|
|
525
529
|
sortBy?: [Extract<keyof M, string>, "asc" | "desc"];
|
|
526
|
-
setSortBy?: (sortBy
|
|
530
|
+
setSortBy?: (sortBy?: [Extract<keyof M, string>, "asc" | "desc"]) => void;
|
|
527
531
|
searchString?: string;
|
|
528
532
|
setSearchString?: (searchString?: string) => void;
|
|
529
533
|
clearFilter?: () => void;
|
package/src/types/datasource.ts
CHANGED
|
@@ -216,6 +216,7 @@ export interface DataSource {
|
|
|
216
216
|
* @param props
|
|
217
217
|
*/
|
|
218
218
|
isFilterCombinationValid?(props: FilterCombinationValidProps): boolean;
|
|
219
|
+
|
|
219
220
|
}
|
|
220
221
|
|
|
221
222
|
export type FilterCombinationValidProps = {
|
|
@@ -366,24 +367,26 @@ export interface DataSourceDelegate {
|
|
|
366
367
|
* Convert a FireCMS reference to a reference that can be used by the datasource
|
|
367
368
|
* @param reference
|
|
368
369
|
*/
|
|
369
|
-
buildReference: (reference: EntityReference) => any,
|
|
370
|
+
// buildReference: (reference: EntityReference) => any,
|
|
370
371
|
|
|
371
372
|
/**
|
|
372
373
|
* Convert a FireCMS GeoPoint to a GeoPoint that can be used by the datasource
|
|
373
374
|
* @param geoPoint
|
|
374
375
|
*/
|
|
375
|
-
buildGeoPoint: (geoPoint: GeoPoint) => any,
|
|
376
|
+
// buildGeoPoint: (geoPoint: GeoPoint) => any,
|
|
376
377
|
|
|
377
378
|
/**
|
|
378
379
|
* Get the object to generate the current time in the datasource
|
|
379
380
|
*/
|
|
380
381
|
currentTime(): any;
|
|
381
382
|
|
|
382
|
-
buildDate: (date: Date) => any;
|
|
383
|
+
// buildDate: (date: Date) => any;
|
|
383
384
|
|
|
384
|
-
buildDeleteFieldValue: () => any;
|
|
385
|
+
// buildDeleteFieldValue: () => any;
|
|
385
386
|
|
|
386
387
|
delegateToCMSModel: (data: any) => any;
|
|
387
388
|
|
|
389
|
+
cmsToDelegateModel: (data: any) => any;
|
|
390
|
+
|
|
388
391
|
setDateToMidnight: (input?: any) => any;
|
|
389
392
|
}
|
package/src/types/entities.ts
CHANGED
|
@@ -37,7 +37,7 @@ export type EntityValues<M extends object> = M;
|
|
|
37
37
|
/**
|
|
38
38
|
* Class used to create a reference to an entity in a different path
|
|
39
39
|
*/
|
|
40
|
-
export class EntityReference
|
|
40
|
+
export class EntityReference {
|
|
41
41
|
/**
|
|
42
42
|
* ID of the entity
|
|
43
43
|
*/
|
|
@@ -78,3 +78,11 @@ export class GeoPoint {
|
|
|
78
78
|
this.longitude = longitude;
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
|
+
|
|
82
|
+
export class Vector {
|
|
83
|
+
readonly value: number[];
|
|
84
|
+
|
|
85
|
+
constructor(value: number[]) {
|
|
86
|
+
this.value = value;
|
|
87
|
+
}
|
|
88
|
+
}
|
package/src/types/index.ts
CHANGED
|
@@ -23,7 +23,6 @@ export * from "./plugins";
|
|
|
23
23
|
export * from "./analytics";
|
|
24
24
|
export * from "./firecms";
|
|
25
25
|
export * from "./roles";
|
|
26
|
-
export * from "./appcheck";
|
|
27
26
|
export * from "./export_import";
|
|
28
27
|
export * from "./modify_collections";
|
|
29
28
|
export * from "./analytics_controller";
|