@firecms/core 3.4.0-canary.63041d0 → 3.4.0-canary.9408c41
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/README.md +4 -0
- package/dist/components/EntityCollectionTable/internal/popup_field/PopupFormField.d.ts +3 -1
- package/dist/components/EntityCollectionView/EntityCollectionBoardView.d.ts +3 -1
- package/dist/components/EntityCollectionView/EntityCollectionView.d.ts +3 -2
- package/dist/components/EntityCollectionView/EntityCollectionViewActions.d.ts +3 -1
- package/dist/components/EntityCollectionView/EntityCollectionViewStartActions.d.ts +3 -1
- package/dist/components/EntityCollectionView/useBoardDataController.d.ts +3 -1
- package/dist/components/ErrorBoundary.d.ts +1 -3
- package/dist/components/LicenseBanner.d.ts +23 -0
- package/dist/components/ReferenceTable/ReferenceSelectionTable.d.ts +10 -1
- package/dist/components/common/useDataSourceTableController.d.ts +4 -2
- package/dist/components/common/useTableSearchHelper.d.ts +3 -1
- package/dist/components/index.d.ts +1 -0
- package/dist/contexts/LicenseStatusContext.d.ts +16 -0
- package/dist/core/EntityEditView.d.ts +2 -0
- package/dist/core/EntityEditViewFormActions.d.ts +1 -1
- package/dist/core/index.d.ts +1 -0
- package/dist/core/plugin_translations.d.ts +38 -0
- package/dist/core/pro_plugins.d.ts +40 -0
- package/dist/de-DMp6cIY1.js +702 -0
- package/dist/de-DMp6cIY1.js.map +1 -0
- package/dist/editor/extensions/Image.d.ts +11 -0
- package/dist/editor/index.d.ts +2 -1
- package/dist/editor/lazy_editor.d.ts +6 -0
- package/dist/editor-BSo_aS1I.js +4563 -0
- package/dist/editor-BSo_aS1I.js.map +1 -0
- package/dist/form/EntityForm.d.ts +8 -1
- package/dist/form/EntityFormActions.d.ts +2 -0
- package/dist/fr-Cckz4IkR.js +702 -0
- package/dist/fr-Cckz4IkR.js.map +1 -0
- package/dist/hi-CDCoIqbu.js +702 -0
- package/dist/hi-CDCoIqbu.js.map +1 -0
- package/dist/hooks/data/delete.d.ts +1 -1
- package/dist/hooks/data/useCollectionFetch.d.ts +7 -1
- package/dist/hooks/data/useEntityFetch.d.ts +4 -1
- package/dist/hooks/index.d.ts +2 -0
- package/dist/hooks/useLicenseStatus.d.ts +16 -0
- package/dist/hooks/useProjectLog.d.ts +11 -6
- package/dist/hooks/useTranslation.d.ts +1 -1
- package/dist/i18n/__tests__/FireCMSi18nProvider.test.d.ts +1 -0
- package/dist/i18n/__tests__/nested_provider_lifecycle.test.d.ts +1 -0
- package/dist/index.es.js +3113 -10785
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +7939 -6836
- package/dist/index.umd.js.map +1 -1
- package/dist/it-BJCUqixj.js +702 -0
- package/dist/it-BJCUqixj.js.map +1 -0
- package/dist/pl-DgT_joFf.js +704 -0
- package/dist/pl-DgT_joFf.js.map +1 -0
- package/dist/pt-B3dN3x0X.js +704 -0
- package/dist/pt-B3dN3x0X.js.map +1 -0
- package/dist/types/analytics.d.ts +1 -1
- package/dist/types/collections.d.ts +7 -0
- package/dist/types/datasource.d.ts +25 -4
- package/dist/types/entities.d.ts +21 -1
- package/dist/types/entity_actions.d.ts +6 -0
- package/dist/types/entity_callbacks.d.ts +21 -0
- package/dist/types/firecms.d.ts +18 -1
- package/dist/types/firecms_context.d.ts +8 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/license.d.ts +54 -0
- package/dist/types/navigation.d.ts +34 -5
- package/dist/types/plugins.d.ts +22 -1
- package/dist/types/side_entity_controller.d.ts +3 -2
- package/dist/types/translations.d.ts +35 -2
- package/dist/util/date_fns_locales.d.ts +18 -0
- package/dist/util/entity_cache.d.ts +11 -0
- package/dist/util/index.d.ts +1 -0
- package/dist/util/lazy_eager.d.ts +3 -3
- package/dist/util/navigation_utils.d.ts +84 -2
- package/dist/util/parent_references_from_path.d.ts +16 -0
- package/dist/util/paths.d.ts +28 -0
- package/dist/util/permissions.d.ts +10 -4
- package/package.json +5 -6
- package/src/app/Scaffold.tsx +7 -0
- package/src/components/DeleteEntityDialog.tsx +2 -0
- package/src/components/EntityCollectionTable/EntityCollectionRowActions.tsx +2 -2
- package/src/components/EntityCollectionTable/internal/popup_field/PopupFormField.tsx +5 -1
- package/src/components/EntityCollectionView/EntityCollectionBoardView.tsx +12 -0
- package/src/components/EntityCollectionView/EntityCollectionView.tsx +49 -16
- package/src/components/EntityCollectionView/EntityCollectionViewActions.tsx +6 -2
- package/src/components/EntityCollectionView/EntityCollectionViewStartActions.tsx +4 -0
- package/src/components/EntityCollectionView/useBoardDataController.tsx +17 -1
- package/src/components/EntityPreview.tsx +4 -1
- package/src/components/ErrorBoundary.tsx +12 -18
- package/src/components/LicenseBanner.tsx +209 -0
- package/src/components/ReferenceTable/ReferenceSelectionTable.tsx +19 -2
- package/src/components/common/default_entity_actions.tsx +22 -2
- package/src/components/common/useDataSourceTableController.tsx +32 -8
- package/src/components/common/useTableSearchHelper.ts +5 -0
- package/src/components/index.tsx +1 -0
- package/src/contexts/LicenseStatusContext.tsx +50 -0
- package/src/core/DefaultAppBar.tsx +28 -16
- package/src/core/EntityEditView.tsx +11 -12
- package/src/core/EntityEditViewFormActions.tsx +3 -2
- package/src/core/EntitySidePanel.tsx +6 -4
- package/src/core/FireCMS.tsx +73 -47
- package/src/core/SideDialogs.tsx +44 -1
- package/src/core/index.tsx +1 -0
- package/src/core/plugin_translations.ts +87 -0
- package/src/core/pro_plugins.ts +79 -0
- package/src/editor/components/SlashCommandMenu.tsx +4 -1
- package/src/editor/extensions/Image.ts +17 -1
- package/src/editor/hooks/useProseMirror.ts +5 -0
- package/src/editor/index.ts +11 -1
- package/src/editor/lazy_editor.tsx +33 -0
- package/src/form/EntityForm.tsx +30 -9
- package/src/form/EntityFormActions.tsx +2 -0
- package/src/hooks/data/delete.ts +8 -0
- package/src/hooks/data/save.ts +5 -2
- package/src/hooks/data/useCollectionFetch.tsx +21 -1
- package/src/hooks/data/useEntityFetch.tsx +28 -6
- package/src/hooks/index.tsx +2 -0
- package/src/hooks/useBuildNavigationController.tsx +70 -15
- package/src/hooks/useFireCMSContext.tsx +7 -3
- package/src/hooks/useLicenseStatus.tsx +32 -0
- package/src/hooks/useProjectLog.tsx +27 -9
- package/src/hooks/useResolvedNavigationFrom.tsx +3 -1
- package/src/hooks/useTranslation.ts +3 -1
- package/src/i18n/FireCMSi18nProvider.tsx +199 -25
- package/src/i18n/__tests__/FireCMSi18nProvider.test.tsx +91 -0
- package/src/i18n/__tests__/nested_provider_lifecycle.test.tsx +67 -0
- package/src/internal/useBuildDataSource.ts +26 -15
- package/src/internal/useBuildSideEntityController.tsx +13 -5
- package/src/locales/de.ts +18 -2
- package/src/locales/en.ts +21 -3
- package/src/locales/es.ts +18 -2
- package/src/locales/fr.ts +18 -2
- package/src/locales/hi.ts +18 -2
- package/src/locales/it.ts +18 -2
- package/src/locales/pl.ts +18 -2
- package/src/locales/pt.ts +18 -2
- package/src/preview/components/DatePreview.tsx +2 -4
- package/src/preview/components/ReferencePreview.tsx +7 -3
- package/src/routes/FireCMSRoute.tsx +9 -4
- package/src/types/analytics.ts +3 -0
- package/src/types/collections.ts +8 -0
- package/src/types/datasource.ts +25 -4
- package/src/types/entities.ts +24 -1
- package/src/types/entity_actions.tsx +6 -0
- package/src/types/entity_callbacks.ts +24 -0
- package/src/types/firecms.tsx +19 -1
- package/src/types/firecms_context.tsx +9 -0
- package/src/types/index.ts +1 -0
- package/src/types/license.ts +62 -0
- package/src/types/navigation.ts +35 -5
- package/src/types/plugins.tsx +23 -1
- package/src/types/side_entity_controller.tsx +3 -2
- package/src/types/translations.ts +35 -2
- package/src/util/date_fns_locales.ts +168 -0
- package/src/util/entities.ts +3 -1
- package/src/util/entity_cache.ts +18 -0
- package/src/util/index.ts +1 -0
- package/src/util/lazy_eager.tsx +30 -8
- package/src/util/navigation_from_path.ts +6 -1
- package/src/util/navigation_utils.ts +210 -3
- package/src/util/parent_references_from_path.ts +42 -3
- package/src/util/paths.ts +40 -3
- package/src/util/permissions.ts +22 -10
- package/src/util/useStorageUploadController.tsx +6 -1
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
|
|
3
3
|
import { format } from "date-fns";
|
|
4
|
-
import * as locales from "date-fns/locale";
|
|
5
4
|
import { useCustomizationController } from "../../hooks";
|
|
6
|
-
import { defaultDateFormat } from "../../util";
|
|
5
|
+
import { defaultDateFormat, useDateFnsLocale } from "../../util";
|
|
7
6
|
|
|
8
7
|
export interface DatePreviewProps {
|
|
9
8
|
date: Date;
|
|
@@ -28,8 +27,7 @@ export function DatePreview({
|
|
|
28
27
|
}: DatePreviewProps): React.ReactElement {
|
|
29
28
|
|
|
30
29
|
const customizationController = useCustomizationController();
|
|
31
|
-
|
|
32
|
-
const dateUtilsLocale = customizationController?.locale ? locales[customizationController?.locale] : undefined;
|
|
30
|
+
const dateUtilsLocale = useDateFnsLocale(customizationController?.locale);
|
|
33
31
|
|
|
34
32
|
if (!date) {
|
|
35
33
|
return <></>;
|
|
@@ -7,6 +7,7 @@ import { Skeleton } from "@firecms/ui";
|
|
|
7
7
|
import { ErrorBoundary, ErrorView } from "../../components";
|
|
8
8
|
import { EntityPreview, EntityPreviewContainer } from "../../components/EntityPreview";
|
|
9
9
|
import { jsonStringifyReplacer } from "../../util/objects";
|
|
10
|
+
import { entityCacheKey } from "../../util/entity_cache";
|
|
10
11
|
|
|
11
12
|
export type ReferencePreviewProps = {
|
|
12
13
|
disabled?: boolean;
|
|
@@ -53,7 +54,7 @@ function ReferencePreviewInternal({
|
|
|
53
54
|
|
|
54
55
|
const navigationController = useNavigationController();
|
|
55
56
|
|
|
56
|
-
const collection = navigationController.getCollection(reference.path);
|
|
57
|
+
const collection = navigationController.getCollection(reference.path, false, reference.pathSegments);
|
|
57
58
|
if (!collection) {
|
|
58
59
|
if (customizationController.components?.missingReference) {
|
|
59
60
|
return <customizationController.components.missingReference path={reference.path}/>;
|
|
@@ -99,16 +100,19 @@ function ReferencePreviewExisting<M extends Record<string, any> = any>({
|
|
|
99
100
|
dataLoadingError
|
|
100
101
|
} = useEntityFetch({
|
|
101
102
|
path: reference.path,
|
|
103
|
+
// References carry the segments of the collection they point at, when the code that
|
|
104
|
+
// built them knew them (see getReferenceFrom).
|
|
105
|
+
pathSegments: reference.pathSegments,
|
|
102
106
|
entityId: reference.id,
|
|
103
107
|
collection,
|
|
104
108
|
useCache: true
|
|
105
109
|
});
|
|
106
110
|
|
|
107
111
|
if (entity) {
|
|
108
|
-
referencesCache.set(reference.
|
|
112
|
+
referencesCache.set(entityCacheKey(reference.path, reference.id), entity);
|
|
109
113
|
}
|
|
110
114
|
|
|
111
|
-
const usedEntity = entity ?? referencesCache.get(reference.
|
|
115
|
+
const usedEntity = entity ?? referencesCache.get(entityCacheKey(reference.path, reference.id));
|
|
112
116
|
|
|
113
117
|
let body: React.ReactNode;
|
|
114
118
|
|
|
@@ -90,7 +90,7 @@ export function FireCMSRoute() {
|
|
|
90
90
|
let collection: EntityCollection<any> | undefined;
|
|
91
91
|
collection = navigation.getCollectionById(navigationEntries[0].id);
|
|
92
92
|
if (!collection)
|
|
93
|
-
collection = navigation.getCollection(navigationEntries[0].path);
|
|
93
|
+
collection = navigation.getCollection(navigationEntries[0].path, false, navigationEntries[0].pathSegments);
|
|
94
94
|
if (!collection)
|
|
95
95
|
return null;
|
|
96
96
|
return <React.Suspense fallback={null}>
|
|
@@ -114,7 +114,7 @@ export function FireCMSRoute() {
|
|
|
114
114
|
const firstEntry = navigationEntries[0] as NavigationViewCollectionInternal<any>;
|
|
115
115
|
collection = navigation.getCollectionById(firstEntry.id);
|
|
116
116
|
if (!collection)
|
|
117
|
-
collection = navigation.getCollection(firstEntry.path);
|
|
117
|
+
collection = navigation.getCollection(firstEntry.path, false, firstEntry.pathSegments);
|
|
118
118
|
if (!collection)
|
|
119
119
|
return null;
|
|
120
120
|
return <React.Suspense fallback={null}>
|
|
@@ -186,6 +186,8 @@ function EntityFullScreenRoute({
|
|
|
186
186
|
const urlTab = getSelectedTabFromUrl(isNew, lastCustomView);
|
|
187
187
|
const [selectedTab, setSelectedTab] = useState<string | undefined>(urlTab);
|
|
188
188
|
|
|
189
|
+
// `navigationPath` comes from the URL, where entity ids are still escaped (see
|
|
190
|
+
// encodeEntityId), so splitting it on "/" inside is correct and no segments are needed.
|
|
189
191
|
const parentCollectionIds = navigation.getParentCollectionIds(navigationPath);
|
|
190
192
|
useEffect(() => {
|
|
191
193
|
if (urlTab !== selectedTab) {
|
|
@@ -250,14 +252,17 @@ function EntityFullScreenRoute({
|
|
|
250
252
|
// `fullIdPath` is used downstream to build URLs, so it carries the escaped chain;
|
|
251
253
|
// `collectionPath` addresses the datasource, so it is resolved from the raw one.
|
|
252
254
|
const fullIdPath = isNew ? lastCollectionEntry!.fullPath : (parentCollectionEntry?.fullPath ?? lastEntityEntry!.fullPath);
|
|
253
|
-
|
|
255
|
+
// The navigation entries know the real segment boundaries, so the path is resolved
|
|
256
|
+
// from them rather than re-parsed out of the flattened string.
|
|
257
|
+
const pathSegments = isNew ? lastCollectionEntry!.pathSegments : lastEntityEntry!.pathSegments;
|
|
258
|
+
const collectionPath = navigation.resolveIdsFrom(isNew ? lastCollectionEntry!.path : lastEntityEntry!.path, pathSegments);
|
|
254
259
|
return <>
|
|
255
260
|
<React.Suspense fallback={null}>
|
|
256
261
|
<EntityEditView
|
|
257
262
|
key={collection.id + "_" + (isNew ? "new" : (isCopy ? entityId + "_copy" : entityId))}
|
|
258
263
|
entityId={isNew ? undefined : entityId}
|
|
259
264
|
fullIdPath={fullIdPath}
|
|
260
|
-
pathSegments={
|
|
265
|
+
pathSegments={pathSegments}
|
|
261
266
|
collection={collection}
|
|
262
267
|
layout={"full_screen"}
|
|
263
268
|
path={collectionPath}
|
package/src/types/analytics.ts
CHANGED
package/src/types/collections.ts
CHANGED
|
@@ -497,6 +497,14 @@ export interface CollectionActionsProps<M extends Record<string, any> = any, USE
|
|
|
497
497
|
*/
|
|
498
498
|
path: string;
|
|
499
499
|
|
|
500
|
+
/**
|
|
501
|
+
* `path` split at its real segment boundaries, e.g. `["nodes", "node/42", "edges"]`.
|
|
502
|
+
*
|
|
503
|
+
* Optional, and never derived by splitting `path` — a parent entity id may itself
|
|
504
|
+
* contain "/". Absent means "not known here", not "no slashes".
|
|
505
|
+
*/
|
|
506
|
+
pathSegments?: string[];
|
|
507
|
+
|
|
500
508
|
/**
|
|
501
509
|
* Path of the last collection, like `addresses`
|
|
502
510
|
*/
|
package/src/types/datasource.ts
CHANGED
|
@@ -106,6 +106,11 @@ export interface SaveEntityProps<M extends Record<string, any> = any> {
|
|
|
106
106
|
*/
|
|
107
107
|
export interface DeleteEntityProps<M extends Record<string, any> = any> {
|
|
108
108
|
entity: Entity<M>;
|
|
109
|
+
/**
|
|
110
|
+
* `entity.path` split at its real segment boundaries, when the caller knows them.
|
|
111
|
+
* Never derived from `entity.path` — see `pathSegments` on FetchCollectionProps.
|
|
112
|
+
*/
|
|
113
|
+
pathSegments?: string[];
|
|
109
114
|
collection?: EntityCollection<M> | ResolvedEntityCollection<M>;
|
|
110
115
|
}
|
|
111
116
|
|
|
@@ -245,13 +250,19 @@ export interface DataSource {
|
|
|
245
250
|
name: string,
|
|
246
251
|
value: any,
|
|
247
252
|
entityId?: string,
|
|
248
|
-
collection?: EntityCollection
|
|
253
|
+
collection?: EntityCollection,
|
|
254
|
+
/**
|
|
255
|
+
* Appended last, and optional, so existing implementations and callers keep
|
|
256
|
+
* working unchanged — a function declaring fewer parameters stays assignable.
|
|
257
|
+
* See `pathSegments` on FetchCollectionProps.
|
|
258
|
+
*/
|
|
259
|
+
pathSegments?: string[]
|
|
249
260
|
): Promise<boolean>;
|
|
250
261
|
|
|
251
262
|
/**
|
|
252
263
|
* Generate an id for a new entity
|
|
253
264
|
*/
|
|
254
|
-
generateEntityId(path: string, collection: EntityCollection): string;
|
|
265
|
+
generateEntityId(path: string, collection: EntityCollection, pathSegments?: string[]): string;
|
|
255
266
|
|
|
256
267
|
/**
|
|
257
268
|
* Count the number of entities in a collection
|
|
@@ -272,6 +283,10 @@ export interface DataSource {
|
|
|
272
283
|
initTextSearch?: (props: {
|
|
273
284
|
context: FireCMSContext,
|
|
274
285
|
path: string,
|
|
286
|
+
/**
|
|
287
|
+
* `path` split at its real segment boundaries, when known. Never derived from `path`.
|
|
288
|
+
*/
|
|
289
|
+
pathSegments?: string[],
|
|
275
290
|
collection: EntityCollection,
|
|
276
291
|
parentCollectionIds?: string[]
|
|
277
292
|
}) => Promise<boolean>;
|
|
@@ -279,6 +294,8 @@ export interface DataSource {
|
|
|
279
294
|
}
|
|
280
295
|
|
|
281
296
|
export type FilterCombinationValidProps = {
|
|
297
|
+
/** `path` split at its real segment boundaries — see `pathSegments` on FetchCollectionProps. */
|
|
298
|
+
pathSegments?: string[];
|
|
282
299
|
path: string;
|
|
283
300
|
collection: EntityCollection<any>;
|
|
284
301
|
filterValues: FilterValues<any>;
|
|
@@ -408,12 +425,12 @@ export interface DataSourceDelegate {
|
|
|
408
425
|
* @param collection
|
|
409
426
|
* @return `true` if there are no other fields besides the given entity
|
|
410
427
|
*/
|
|
411
|
-
checkUniqueField(path: string, name: string, value: any, entityId?: string, collection?: EntityCollection): Promise<boolean>;
|
|
428
|
+
checkUniqueField(path: string, name: string, value: any, entityId?: string, collection?: EntityCollection, pathSegments?: string[]): Promise<boolean>;
|
|
412
429
|
|
|
413
430
|
/**
|
|
414
431
|
* Generate an id for a new entity
|
|
415
432
|
*/
|
|
416
|
-
generateEntityId(path: string, collection?: EntityCollection): string;
|
|
433
|
+
generateEntityId(path: string, collection?: EntityCollection, pathSegments?: string[]): string;
|
|
417
434
|
|
|
418
435
|
/**
|
|
419
436
|
* Count the number of entities in a collection
|
|
@@ -440,6 +457,10 @@ export interface DataSourceDelegate {
|
|
|
440
457
|
initTextSearch?: (props: {
|
|
441
458
|
context: FireCMSContext,
|
|
442
459
|
path: string,
|
|
460
|
+
/**
|
|
461
|
+
* `path` split at its real segment boundaries, when known. Never derived from `path`.
|
|
462
|
+
*/
|
|
463
|
+
pathSegments?: string[],
|
|
443
464
|
databaseId?: string,
|
|
444
465
|
collection: EntityCollection,
|
|
445
466
|
parentCollectionIds?: string[]
|
package/src/types/entities.ts
CHANGED
|
@@ -21,6 +21,17 @@ export interface Entity<M extends object = any> {
|
|
|
21
21
|
*/
|
|
22
22
|
path: string;
|
|
23
23
|
|
|
24
|
+
/**
|
|
25
|
+
* `path` split at its real segment boundaries, e.g. `["nodes", "node/42", "edges"]`.
|
|
26
|
+
*
|
|
27
|
+
* `path` is a single flattened string, so a parent entity id containing "/" cannot be
|
|
28
|
+
* recovered from it. This keeps each segment whole.
|
|
29
|
+
*
|
|
30
|
+
* Optional, and never derived by splitting `path` — a guess would be wrong in exactly
|
|
31
|
+
* the case the field exists for. Absent means "not known here", not "no slashes".
|
|
32
|
+
*/
|
|
33
|
+
pathSegments?: string[];
|
|
34
|
+
|
|
24
35
|
/**
|
|
25
36
|
* Current values
|
|
26
37
|
*/
|
|
@@ -55,10 +66,22 @@ export class EntityReference {
|
|
|
55
66
|
*/
|
|
56
67
|
readonly databaseId?: string;
|
|
57
68
|
|
|
58
|
-
|
|
69
|
+
/**
|
|
70
|
+
* `path` split at its real segment boundaries, e.g. `["nodes", "node/42", "edges"]`.
|
|
71
|
+
*
|
|
72
|
+
* `path` is a single flattened string, so a parent entity id containing "/" cannot be
|
|
73
|
+
* recovered from it. This keeps each segment whole.
|
|
74
|
+
*
|
|
75
|
+
* Optional, and never derived by splitting `path` — a guess would be wrong in exactly
|
|
76
|
+
* the case the field exists for. Absent means "not known here", not "no slashes".
|
|
77
|
+
*/
|
|
78
|
+
readonly pathSegments?: string[];
|
|
79
|
+
|
|
80
|
+
constructor(id: string, path: string, databaseId?: string, pathSegments?: string[]) {
|
|
59
81
|
this.id = id;
|
|
60
82
|
this.path = path;
|
|
61
83
|
this.databaseId = databaseId;
|
|
84
|
+
this.pathSegments = pathSegments;
|
|
62
85
|
}
|
|
63
86
|
|
|
64
87
|
get pathWithId() {
|
|
@@ -65,6 +65,12 @@ export type EntityActionClickProps<M extends object, USER extends User = User> =
|
|
|
65
65
|
context: FireCMSContext<USER>;
|
|
66
66
|
|
|
67
67
|
fullPath?: string;
|
|
68
|
+
/**
|
|
69
|
+
* `fullPath` split at its real segment boundaries, e.g. `["nodes", "node/42", "edges"]`,
|
|
70
|
+
* when the caller knows them. Absent means "not known here" — never derived from
|
|
71
|
+
* `fullPath`, since a parent id may itself contain "/".
|
|
72
|
+
*/
|
|
73
|
+
pathSegments?: string[];
|
|
68
74
|
fullIdPath?: string;
|
|
69
75
|
collection?: EntityCollection<M>;
|
|
70
76
|
|
|
@@ -86,6 +86,14 @@ export interface EntityOnFetchProps<M extends Record<string, any> = any, USER ex
|
|
|
86
86
|
*/
|
|
87
87
|
path: string;
|
|
88
88
|
|
|
89
|
+
/**
|
|
90
|
+
* `path` split at its real segment boundaries, e.g. `["nodes", "node/42", "edges"]`.
|
|
91
|
+
*
|
|
92
|
+
* Optional, and never derived by splitting `path` — a parent entity id may itself
|
|
93
|
+
* contain "/". Absent means "not known here", not "no slashes".
|
|
94
|
+
*/
|
|
95
|
+
pathSegments?: string[];
|
|
96
|
+
|
|
89
97
|
/**
|
|
90
98
|
* Fetched entity
|
|
91
99
|
*/
|
|
@@ -133,6 +141,14 @@ export interface EntityOnSaveProps<M extends Record<string, any> = any, USER ext
|
|
|
133
141
|
*/
|
|
134
142
|
path: string;
|
|
135
143
|
|
|
144
|
+
/**
|
|
145
|
+
* `path` split at its real segment boundaries, e.g. `["nodes", "node/42", "edges"]`.
|
|
146
|
+
*
|
|
147
|
+
* Optional, and never derived by splitting `path` — a parent entity id may itself
|
|
148
|
+
* contain "/". Absent means "not known here", not "no slashes".
|
|
149
|
+
*/
|
|
150
|
+
pathSegments?: string[];
|
|
151
|
+
|
|
136
152
|
/**
|
|
137
153
|
* Full path where this entity is being saved, with alias resolved
|
|
138
154
|
*/
|
|
@@ -180,6 +196,14 @@ export interface EntityOnDeleteProps<M extends Record<string, any> = any, USER e
|
|
|
180
196
|
*/
|
|
181
197
|
path: string;
|
|
182
198
|
|
|
199
|
+
/**
|
|
200
|
+
* `path` split at its real segment boundaries, e.g. `["nodes", "node/42", "edges"]`.
|
|
201
|
+
*
|
|
202
|
+
* Optional, and never derived by splitting `path` — a parent entity id may itself
|
|
203
|
+
* contain "/". Absent means "not known here", not "no slashes".
|
|
204
|
+
*/
|
|
205
|
+
pathSegments?: string[];
|
|
206
|
+
|
|
183
207
|
/**
|
|
184
208
|
* Deleted entity id
|
|
185
209
|
*/
|
package/src/types/firecms.tsx
CHANGED
|
@@ -70,10 +70,28 @@ export type FireCMSProps<USER extends User> = {
|
|
|
70
70
|
}) => React.ReactNode;
|
|
71
71
|
|
|
72
72
|
/**
|
|
73
|
-
*
|
|
73
|
+
* Your FireCMS PRO license key. It is checked against
|
|
74
|
+
* `https://api.firecms.co/access_log` when a user signs in.
|
|
74
75
|
*/
|
|
75
76
|
apiKey?: string;
|
|
76
77
|
|
|
78
|
+
/**
|
|
79
|
+
* On sign-in FireCMS sends one request to `https://api.firecms.co/access_log`
|
|
80
|
+
* with the user's ID token (which carries the Firebase project id, the uid
|
|
81
|
+
* and the email), the user's email, the datasource key and the keys of the
|
|
82
|
+
* mounted plugins; the browser adds the page's referrer. It is how the
|
|
83
|
+
* FireCMS PRO license and trial are checked. No database credentials or
|
|
84
|
+
* content are sent.
|
|
85
|
+
*
|
|
86
|
+
* Set to `false` to skip that request. This only takes effect when the
|
|
87
|
+
* app has no `apiKey` and mounts no PRO plugin (collection editor, user
|
|
88
|
+
* management, import/export, entity history, data enhancement, DataTalk):
|
|
89
|
+
* otherwise the request is the license check and is always sent.
|
|
90
|
+
*
|
|
91
|
+
* Defaults to `true`.
|
|
92
|
+
*/
|
|
93
|
+
telemetry?: boolean;
|
|
94
|
+
|
|
77
95
|
/**
|
|
78
96
|
* Record of custom form fields to be used in the CMS.
|
|
79
97
|
* You can use the key to reference the custom field in
|
|
@@ -11,6 +11,7 @@ import { DialogsController } from "./dialogs_controller";
|
|
|
11
11
|
import { CustomizationController } from "./customization_controller";
|
|
12
12
|
import { AnalyticsController } from "./analytics_controller";
|
|
13
13
|
import { InternalUserManagement } from "./internal_user_management";
|
|
14
|
+
import { AccessResponse } from "./license";
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
17
|
* Context that includes the internal controllers and contexts used by the app.
|
|
@@ -97,4 +98,12 @@ export type FireCMSContext<USER extends User = User, AuthControllerType extends
|
|
|
97
98
|
*/
|
|
98
99
|
userManagement: InternalUserManagement<USER>
|
|
99
100
|
|
|
101
|
+
/**
|
|
102
|
+
* FireCMS license status of this project, `null` until the license check
|
|
103
|
+
* answers or when it is not sent. PRO plugins are paused while `blocked`
|
|
104
|
+
* is true.
|
|
105
|
+
* @see useLicenseStatus
|
|
106
|
+
*/
|
|
107
|
+
licenseStatus?: AccessResponse | null;
|
|
108
|
+
|
|
100
109
|
};
|
package/src/types/index.ts
CHANGED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the FireCMS license check says about this project.
|
|
3
|
+
*
|
|
4
|
+
* - `not_required`: no PRO plugin is mounted.
|
|
5
|
+
* - `whitelisted`: the project is exempt from licensing.
|
|
6
|
+
* - `licensed`: a license covers this project.
|
|
7
|
+
* - `trial`: PRO plugins run on the free trial; see `daysLeft` and `trialEndsAt`.
|
|
8
|
+
* - `expired`: the trial ended without a license. PRO plugins are paused.
|
|
9
|
+
* - `invalid_project`: a license key was presented, but this project is not
|
|
10
|
+
* linked to that license. PRO plugins are paused.
|
|
11
|
+
* - `over_quota`: the license has more projects linked than it pays for.
|
|
12
|
+
* Nothing is paused.
|
|
13
|
+
*
|
|
14
|
+
* @group Models
|
|
15
|
+
*/
|
|
16
|
+
export type LicenseState =
|
|
17
|
+
| "not_required"
|
|
18
|
+
| "whitelisted"
|
|
19
|
+
| "licensed"
|
|
20
|
+
| "trial"
|
|
21
|
+
| "expired"
|
|
22
|
+
| "invalid_project"
|
|
23
|
+
| "over_quota";
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Response of the FireCMS license check (`POST https://api.firecms.co/access_log`).
|
|
27
|
+
*
|
|
28
|
+
* Servers older than the PRO relaunch send only `blocked` and `message`, so
|
|
29
|
+
* treat a missing `licenseState` as unknown. `blocked` is honoured either way.
|
|
30
|
+
*
|
|
31
|
+
* @group Models
|
|
32
|
+
*/
|
|
33
|
+
export type AccessResponse = {
|
|
34
|
+
/**
|
|
35
|
+
* True when the PRO plugins must be paused. The core CMS is never blocked.
|
|
36
|
+
*/
|
|
37
|
+
blocked?: boolean;
|
|
38
|
+
licenseState?: LicenseState;
|
|
39
|
+
/**
|
|
40
|
+
* Number of projects the license pays for. Present with `licensed` and `over_quota`.
|
|
41
|
+
*/
|
|
42
|
+
licensedProjects?: number;
|
|
43
|
+
/**
|
|
44
|
+
* Number of projects linked to the license.
|
|
45
|
+
*/
|
|
46
|
+
linkedProjects?: number;
|
|
47
|
+
projectId?: string;
|
|
48
|
+
licenseId?: string;
|
|
49
|
+
/**
|
|
50
|
+
* ISO date the trial ends or ended. Present with `trial` and `expired`.
|
|
51
|
+
*/
|
|
52
|
+
trialEndsAt?: string;
|
|
53
|
+
/**
|
|
54
|
+
* Whole days left in the trial, present with `trial`.
|
|
55
|
+
*/
|
|
56
|
+
daysLeft?: number;
|
|
57
|
+
/**
|
|
58
|
+
* Where to get or update a license for this project.
|
|
59
|
+
*/
|
|
60
|
+
subscribeUrl?: string;
|
|
61
|
+
message?: string;
|
|
62
|
+
};
|
package/src/types/navigation.ts
CHANGED
|
@@ -57,8 +57,12 @@ export type NavigationController<EC extends EntityCollection = EntityCollection<
|
|
|
57
57
|
/**
|
|
58
58
|
* Get the collection configuration for a given path.
|
|
59
59
|
* The collection is resolved from the given path or alias.
|
|
60
|
+
*
|
|
61
|
+
* Pass `pathSegments` when you have them: without them the path is split on "/" and
|
|
62
|
+
* asserted to have an odd number of parts, which a chain containing a slash-bearing
|
|
63
|
+
* entity id fails — it throws instead of resolving.
|
|
60
64
|
*/
|
|
61
|
-
getCollection: (pathOrId: string, includeUserOverride?: boolean) => EC | undefined;
|
|
65
|
+
getCollection: (pathOrId: string, includeUserOverride?: boolean, pathSegments?: string[]) => EC | undefined;
|
|
62
66
|
|
|
63
67
|
/**
|
|
64
68
|
* Get the top level collection configuration for a given id
|
|
@@ -118,9 +122,30 @@ export type NavigationController<EC extends EntityCollection = EntityCollection<
|
|
|
118
122
|
/**
|
|
119
123
|
* Turn a path with collection ids into a resolved path.
|
|
120
124
|
* The ids (typically used in urls) will be replaced with relative paths (typically used in database paths)
|
|
125
|
+
*
|
|
126
|
+
* Pass `pathSegments` whenever you have them. Without them this has to find the entity
|
|
127
|
+
* ids inside the flattened string by reading up to the next "/", which is wrong for any
|
|
128
|
+
* id that contains one — the chain then shifts by a segment and resolution fails.
|
|
129
|
+
*
|
|
121
130
|
* @param pathWithAliases
|
|
131
|
+
* @param pathSegments `pathWithAliases` split at its real segment boundaries, if known
|
|
122
132
|
*/
|
|
123
|
-
resolveIdsFrom: (pathWithAliases: string) => string;
|
|
133
|
+
resolveIdsFrom: (pathWithAliases: string, pathSegments?: string[]) => string;
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* The segment-wise counterpart of {@link resolveIdsFrom}: replaces collection ids with
|
|
137
|
+
* their real paths while carrying every entity id through whole, slashes included.
|
|
138
|
+
*
|
|
139
|
+
* Idempotent — segments that are already resolved pass through unchanged.
|
|
140
|
+
*
|
|
141
|
+
* Optional so that a `navigationController` built against an earlier version keeps
|
|
142
|
+
* working: callers fall back to using the segments as given, which is what happened
|
|
143
|
+
* before this method existed. Controllers built by `useBuildNavigationController`
|
|
144
|
+
* always provide it.
|
|
145
|
+
*
|
|
146
|
+
* @param pathSegments
|
|
147
|
+
*/
|
|
148
|
+
resolveSegmentsFrom?: (pathSegments: string[]) => string[];
|
|
124
149
|
|
|
125
150
|
/**
|
|
126
151
|
* Call this method to recalculate the navigation
|
|
@@ -128,16 +153,21 @@ export type NavigationController<EC extends EntityCollection = EntityCollection<
|
|
|
128
153
|
refreshNavigation: () => void;
|
|
129
154
|
|
|
130
155
|
/**
|
|
131
|
-
* Retrieve all the related parent references for a given path
|
|
156
|
+
* Retrieve all the related parent references for a given path.
|
|
157
|
+
*
|
|
158
|
+
* `path` is expected escaped, as it comes from the URL. Pass `pathSegments` when the
|
|
159
|
+
* path at hand is a raw datasource path, whose ids may contain a bare "/".
|
|
160
|
+
*
|
|
132
161
|
* @param path
|
|
162
|
+
* @param pathSegments `path` split at its real segment boundaries, with raw ids
|
|
133
163
|
*/
|
|
134
|
-
getParentReferencesFromPath: (path: string) => EntityReference[];
|
|
164
|
+
getParentReferencesFromPath: (path: string, pathSegments?: string[]) => EntityReference[];
|
|
135
165
|
|
|
136
166
|
/**
|
|
137
167
|
* Retrieve all the related parent collection ids for a given path
|
|
138
168
|
* @param path
|
|
139
169
|
*/
|
|
140
|
-
getParentCollectionIds: (path: string) => string[];
|
|
170
|
+
getParentCollectionIds: (path: string, pathSegments?: string[]) => string[];
|
|
141
171
|
|
|
142
172
|
/**
|
|
143
173
|
* Resolve paths from a list of ids
|
package/src/types/plugins.tsx
CHANGED
|
@@ -15,6 +15,9 @@ import { InternalUserManagement } from "./internal_user_management";
|
|
|
15
15
|
* NOTE: This is a work in progress and the API is not stable yet.
|
|
16
16
|
* @group Core
|
|
17
17
|
*/
|
|
18
|
+
/** The strings a plugin contributes for one locale. */
|
|
19
|
+
export type PluginTranslations = Record<string, string>;
|
|
20
|
+
|
|
18
21
|
export type FireCMSPlugin<PROPS = any, FORM_PROPS = any, EC extends EntityCollection = EntityCollection, COL_ACTIONS_PROPS = any, COL_ACTIONS_START__PROPS = any> = {
|
|
19
22
|
|
|
20
23
|
/**
|
|
@@ -33,8 +36,14 @@ export type FireCMSPlugin<PROPS = any, FORM_PROPS = any, EC extends EntityCollec
|
|
|
33
36
|
* locales, specific for this plugin.
|
|
34
37
|
* Keys can be existing `FireCMSTranslations` keys, or new keys if using
|
|
35
38
|
* module augmentation.
|
|
39
|
+
*
|
|
40
|
+
* A value may be the strings themselves, or a function returning a promise
|
|
41
|
+
* of them. Use the function form for locales you ship in the plugin bundle:
|
|
42
|
+
* only the active language is ever read, so importing all of them statically
|
|
43
|
+
* puts every language into the bundle of every app that mounts the plugin.
|
|
44
|
+
* Both forms are supported; a plain record keeps working unchanged.
|
|
36
45
|
*/
|
|
37
|
-
i18n?: Record<string,
|
|
46
|
+
i18n?: Record<string, PluginTranslations | (() => Promise<PluginTranslations>)>;
|
|
38
47
|
|
|
39
48
|
/**
|
|
40
49
|
* You can use this prop to add higher order components to the CMS.
|
|
@@ -146,6 +155,8 @@ export type FireCMSPlugin<PROPS = any, FORM_PROPS = any, EC extends EntityCollec
|
|
|
146
155
|
blockSearch?: (props: {
|
|
147
156
|
context: FireCMSContext,
|
|
148
157
|
path: string,
|
|
158
|
+
/** `path` split at its real segment boundaries, when known. */
|
|
159
|
+
pathSegments?: string[],
|
|
149
160
|
collection: EC,
|
|
150
161
|
parentCollectionIds?: string[]
|
|
151
162
|
}) => boolean;
|
|
@@ -153,6 +164,8 @@ export type FireCMSPlugin<PROPS = any, FORM_PROPS = any, EC extends EntityCollec
|
|
|
153
164
|
showTextSearchBar?: (props: {
|
|
154
165
|
context: FireCMSContext,
|
|
155
166
|
path: string,
|
|
167
|
+
/** `path` split at its real segment boundaries, when known. */
|
|
168
|
+
pathSegments?: string[],
|
|
156
169
|
collection: EC,
|
|
157
170
|
parentCollectionIds?: string[]
|
|
158
171
|
}) => boolean;
|
|
@@ -160,6 +173,8 @@ export type FireCMSPlugin<PROPS = any, FORM_PROPS = any, EC extends EntityCollec
|
|
|
160
173
|
onTextSearchClick?: (props: {
|
|
161
174
|
context: FireCMSContext,
|
|
162
175
|
path: string,
|
|
176
|
+
/** `path` split at its real segment boundaries, when known. */
|
|
177
|
+
pathSegments?: string[],
|
|
163
178
|
collection: EC,
|
|
164
179
|
parentCollectionIds?: string[]
|
|
165
180
|
}) => Promise<boolean>;
|
|
@@ -308,6 +323,13 @@ export interface PluginHomePageActionsProps<EP extends object = object, M extend
|
|
|
308
323
|
export interface PluginFormActionProps<USER extends User = User, EC extends EntityCollection = EntityCollection> {
|
|
309
324
|
entityId?: string;
|
|
310
325
|
path: string;
|
|
326
|
+
/**
|
|
327
|
+
* `path` split at its real segment boundaries, e.g. `["nodes", "node/42", "edges"]`.
|
|
328
|
+
*
|
|
329
|
+
* Optional, and never derived by splitting `path` — a parent entity id may itself
|
|
330
|
+
* contain "/". Absent means "not known here", not "no slashes".
|
|
331
|
+
*/
|
|
332
|
+
pathSegments?: string[];
|
|
311
333
|
parentCollectionIds: string[];
|
|
312
334
|
status: EntityStatus;
|
|
313
335
|
collection: EC;
|
|
@@ -21,8 +21,9 @@ export interface EntitySidePanelProps<M extends Record<string, any> = any> {
|
|
|
21
21
|
fullIdPath?: string;
|
|
22
22
|
/**
|
|
23
23
|
* `path` split at its real segment boundaries, e.g. `["nodes", "node/42", "edges"]`.
|
|
24
|
-
*
|
|
25
|
-
*
|
|
24
|
+
*
|
|
25
|
+
* Optional, and never derived by splitting `path` — a guess would be wrong in exactly
|
|
26
|
+
* the case the field exists for. Absent means "not known here", not "no slashes".
|
|
26
27
|
*/
|
|
27
28
|
pathSegments?: string[];
|
|
28
29
|
|
|
@@ -66,8 +66,32 @@ export interface FireCMSTranslations {
|
|
|
66
66
|
select_from: string;
|
|
67
67
|
done: string;
|
|
68
68
|
log_out: string;
|
|
69
|
-
|
|
70
|
-
|
|
69
|
+
/** License banner during the PRO trial. `{{days}}` is the number of days left; plural form. */
|
|
70
|
+
license_trial_banner: string;
|
|
71
|
+
/** `license_trial_banner` when one day is left (i18next `_one` plural form). */
|
|
72
|
+
license_trial_banner_one: string;
|
|
73
|
+
/** License banner once the trial has ended. `{{date}}` is the day it ended. */
|
|
74
|
+
license_expired_banner: string;
|
|
75
|
+
/** `license_expired_banner` when the end date is not known. */
|
|
76
|
+
license_expired_banner_undated: string;
|
|
77
|
+
/** License banner when the license key is not linked to this project. `{{projectId}}` is the Firebase project id. */
|
|
78
|
+
license_invalid_project_banner: string;
|
|
79
|
+
/** `license_invalid_project_banner` when the project id is not known. */
|
|
80
|
+
license_invalid_project_banner_unnamed: string;
|
|
81
|
+
/** License banner when more projects are linked than the license covers. `{{licensed}}` covered, `{{actual}}` linked; plural form. */
|
|
82
|
+
license_over_quota_banner: string;
|
|
83
|
+
/** `license_over_quota_banner` when the license covers one project (i18next `_one` plural form). */
|
|
84
|
+
license_over_quota_banner_one: string;
|
|
85
|
+
/** Link in the license banner to buy a license. */
|
|
86
|
+
license_get_license: string;
|
|
87
|
+
/** Link in the over-quota banner to the license settings. */
|
|
88
|
+
license_update_license: string;
|
|
89
|
+
/** Link in the invalid-project banner to link this project to the license. */
|
|
90
|
+
license_add_project_to_license: string;
|
|
91
|
+
/** Title of the notice a PRO view shows while PRO features are paused. */
|
|
92
|
+
pro_features_paused: string;
|
|
93
|
+
/** Users and Roles views while PRO features are paused. */
|
|
94
|
+
user_management_paused_description: string;
|
|
71
95
|
column_cannot_be_edited: string;
|
|
72
96
|
close: string;
|
|
73
97
|
unsaved_local_changes: string;
|
|
@@ -421,6 +445,10 @@ export interface FireCMSTranslations {
|
|
|
421
445
|
include_undefined_values: string;
|
|
422
446
|
/** Toggle in the export dialog, only shown when a filter or sort is active */
|
|
423
447
|
export_apply_filter_sort: string;
|
|
448
|
+
/** Choice in the export dialog, only shown when entities are selected */
|
|
449
|
+
export_all_entities: string;
|
|
450
|
+
/** Same choice; `{{count}}` is the number of selected entities */
|
|
451
|
+
export_selected_entities: string;
|
|
424
452
|
submit: string;
|
|
425
453
|
|
|
426
454
|
no_filterable_properties: string;
|
|
@@ -470,6 +498,8 @@ export interface FireCMSTranslations {
|
|
|
470
498
|
roles_menu: string;
|
|
471
499
|
project_settings: string;
|
|
472
500
|
firestore_manager: string;
|
|
501
|
+
/** The DataTalk product name, keyed so a locale can transliterate it. */
|
|
502
|
+
datatalk: string;
|
|
473
503
|
manage_your_firestore_data: string;
|
|
474
504
|
|
|
475
505
|
// ─── FireCMS Cloud Login ──────────────────────────────────────
|
|
@@ -486,6 +516,7 @@ export interface FireCMSTranslations {
|
|
|
486
516
|
not_the_admin_panel: string;
|
|
487
517
|
join_our_newsletter: string;
|
|
488
518
|
by_signing_in_you_agree_to_our: string;
|
|
519
|
+
please_accept_the_terms_to_continue: string;
|
|
489
520
|
terms_and_conditions: string;
|
|
490
521
|
and_our: string;
|
|
491
522
|
privacy_policy: string;
|
|
@@ -528,6 +559,8 @@ export interface FireCMSTranslations {
|
|
|
528
559
|
licensed_projects_lowercase: string;
|
|
529
560
|
manage: string;
|
|
530
561
|
update_payment_method: string;
|
|
562
|
+
/** Snackbar when a Stripe billing portal link can't be created. `{{message}}` is the reason. */
|
|
563
|
+
stripe_portal_error: string;
|
|
531
564
|
your_firecms_cloud_projects: string;
|
|
532
565
|
status_active: string;
|
|
533
566
|
status_trialing: string;
|