@firecms/core 3.3.0 → 3.4.0-canary.00f9be5
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/app/useApp.d.ts +1 -0
- package/dist/components/EntityCollectionTable/column_utils.d.ts +4 -3
- package/dist/components/EntityCollectionTable/internal/CollectionTableToolbar.d.ts +6 -1
- 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 +8 -0
- package/dist/components/EntityCollectionView/EntityCollectionViewActions.d.ts +3 -1
- package/dist/components/EntityCollectionView/EntityCollectionViewStartActions.d.ts +3 -1
- package/dist/components/EntityCollectionView/FiltersDialog.d.ts +2 -1
- package/dist/components/EntityCollectionView/useBoardDataController.d.ts +3 -1
- package/dist/components/EntityPreview.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/SelectableTable/filters/filter_text_input.d.ts +30 -0
- package/dist/components/SelectableTable/filters/filter_text_input.test.d.ts +1 -0
- package/dist/components/common/table_url_params.d.ts +24 -0
- package/dist/components/common/useDataSourceTableController.d.ts +9 -1
- package/dist/components/common/useTableSearchHelper.d.ts +9 -1
- package/dist/components/index.d.ts +1 -0
- package/dist/contexts/LicenseStatusContext.d.ts +16 -0
- package/dist/core/DefaultDrawer.d.ts +10 -3
- package/dist/core/EntityEditView.d.ts +9 -1
- package/dist/core/EntityEditViewFormActions.d.ts +1 -1
- package/dist/core/field_configs.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-BF5vcQPY.js +703 -0
- package/dist/de-BF5vcQPY.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/form/field_bindings/GeopointFieldBinding.d.ts +5 -0
- package/dist/form/index.d.ts +1 -0
- package/dist/fr-DH9_wyFI.js +703 -0
- package/dist/fr-DH9_wyFI.js.map +1 -0
- package/dist/hi-BM8dm1Lu.js +703 -0
- package/dist/hi-BM8dm1Lu.js.map +1 -0
- package/dist/hooks/data/delete.d.ts +1 -1
- package/dist/hooks/data/save.d.ts +1 -1
- package/dist/hooks/data/useCollectionFetch.d.ts +7 -1
- package/dist/hooks/data/useEntityFetch.d.ts +9 -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 +4516 -10668
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +9403 -6774
- package/dist/index.umd.js.map +1 -1
- package/dist/it-D3K1gfe4.js +703 -0
- package/dist/it-D3K1gfe4.js.map +1 -0
- package/dist/locales/pl.d.ts +2 -0
- package/dist/pl-CP1zeLzn.js +705 -0
- package/dist/pl-CP1zeLzn.js.map +1 -0
- package/dist/preview/index.d.ts +1 -0
- package/dist/preview/property_previews/GeopointPropertyPreview.d.ts +4 -0
- package/dist/pt-Cus7eKQR.js +705 -0
- package/dist/pt-Cus7eKQR.js.map +1 -0
- package/dist/types/analytics.d.ts +1 -1
- package/dist/types/collections.d.ts +31 -0
- package/dist/types/datasource.d.ts +67 -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 +55 -0
- package/dist/types/navigation.d.ts +34 -5
- package/dist/types/plugins.d.ts +22 -1
- package/dist/types/properties.d.ts +17 -1
- package/dist/types/side_entity_controller.d.ts +7 -0
- package/dist/types/translations.d.ts +43 -2
- package/dist/util/__tests__/collections.test.d.ts +1 -0
- package/dist/util/__tests__/urls.test.d.ts +1 -0
- package/dist/util/collections.d.ts +1 -1
- package/dist/util/date_fns_locales.d.ts +18 -0
- package/dist/util/entities.d.ts +1 -0
- package/dist/util/entity_cache.d.ts +11 -0
- package/dist/util/geopoint.d.ts +22 -0
- package/dist/util/index.d.ts +3 -0
- package/dist/util/lazy_eager.d.ts +3 -3
- package/dist/util/navigation_blocking.d.ts +22 -0
- package/dist/util/navigation_from_path.d.ts +17 -0
- package/dist/util/navigation_utils.d.ts +107 -3
- 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/dist/util/urls.d.ts +22 -0
- package/package.json +17 -11
- package/src/app/Scaffold.tsx +41 -44
- package/src/app/useApp.tsx +1 -0
- package/src/components/DeleteEntityDialog.tsx +2 -0
- package/src/components/EntityCollectionTable/EntityCollectionRowActions.tsx +2 -2
- package/src/components/EntityCollectionTable/EntityCollectionTable.tsx +3 -1
- package/src/components/EntityCollectionTable/column_utils.tsx +11 -19
- package/src/components/EntityCollectionTable/fields/TableReferenceField.tsx +1 -1
- package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +7 -0
- 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 +66 -15
- package/src/components/EntityCollectionView/EntityCollectionViewActions.tsx +6 -2
- package/src/components/EntityCollectionView/EntityCollectionViewStartActions.tsx +8 -1
- package/src/components/EntityCollectionView/FiltersDialog.tsx +39 -28
- package/src/components/EntityCollectionView/useBoardDataController.tsx +17 -1
- package/src/components/EntityPreview.tsx +45 -41
- package/src/components/ErrorBoundary.tsx +12 -18
- package/src/components/LicenseBanner.tsx +207 -0
- package/src/components/ReferenceTable/ReferenceSelectionTable.tsx +19 -2
- package/src/components/ReferenceWidget.tsx +1 -1
- package/src/components/SelectableTable/filters/ReferenceFilterField.tsx +2 -2
- package/src/components/SelectableTable/filters/StringNumberFilterField.tsx +33 -7
- package/src/components/SelectableTable/filters/filter_text_input.test.ts +184 -0
- package/src/components/SelectableTable/filters/filter_text_input.ts +71 -0
- package/src/components/VirtualTable/VirtualTable.tsx +19 -8
- package/src/components/common/default_entity_actions.tsx +22 -2
- package/src/components/common/table_url_params.ts +172 -0
- package/src/components/common/useDataSourceTableController.tsx +126 -163
- package/src/components/common/useTableSearchHelper.ts +26 -2
- package/src/components/index.tsx +1 -0
- package/src/contexts/LicenseStatusContext.tsx +50 -0
- package/src/contexts/SnackbarProvider.tsx +14 -1
- package/src/core/DefaultAppBar.tsx +28 -16
- package/src/core/DefaultDrawer.tsx +150 -64
- package/src/core/DrawerNavigationGroup.tsx +27 -29
- package/src/core/DrawerNavigationItem.tsx +10 -12
- package/src/core/EntityEditView.tsx +77 -35
- package/src/core/EntityEditViewFormActions.tsx +3 -2
- package/src/core/EntitySidePanel.tsx +8 -4
- package/src/core/FireCMS.tsx +73 -47
- package/src/core/SideDialogs.tsx +44 -1
- package/src/core/field_configs.tsx +15 -0
- 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 +31 -10
- package/src/form/EntityFormActions.tsx +2 -0
- package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +1 -1
- package/src/form/field_bindings/GeopointFieldBinding.tsx +139 -0
- package/src/form/index.tsx +1 -0
- package/src/hooks/data/delete.ts +8 -0
- package/src/hooks/data/save.ts +6 -1
- package/src/hooks/data/useCollectionFetch.tsx +21 -1
- package/src/hooks/data/useEntityFetch.tsx +35 -4
- 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/hooks/useValidateAuthenticator.tsx +4 -0
- package/src/i18n/FireCMSi18nProvider.tsx +199 -23
- 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 +37 -15
- package/src/internal/useBuildSideEntityController.tsx +20 -5
- package/src/locales/de.ts +26 -4
- package/src/locales/en.ts +30 -6
- package/src/locales/es.ts +26 -4
- package/src/locales/fr.ts +26 -4
- package/src/locales/hi.ts +26 -4
- package/src/locales/it.ts +26 -4
- package/src/locales/pl.ts +752 -0
- package/src/locales/pt.ts +26 -4
- package/src/preview/PropertyPreview.tsx +12 -0
- package/src/preview/components/DatePreview.tsx +2 -4
- package/src/preview/components/ReferencePreview.tsx +7 -3
- package/src/preview/components/StorageThumbnail.tsx +24 -2
- package/src/preview/index.ts +1 -0
- package/src/preview/property_previews/GeopointPropertyPreview.tsx +23 -0
- package/src/routes/FireCMSRoute.tsx +54 -27
- package/src/types/analytics.ts +3 -0
- package/src/types/collections.ts +34 -0
- package/src/types/datasource.ts +67 -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 +63 -0
- package/src/types/navigation.ts +35 -5
- package/src/types/plugins.tsx +23 -1
- package/src/types/properties.ts +17 -0
- package/src/types/side_entity_controller.tsx +7 -0
- package/src/types/translations.ts +43 -2
- package/src/util/__tests__/collections.test.ts +32 -0
- package/src/util/__tests__/urls.test.ts +131 -0
- package/src/util/collections.ts +1 -1
- package/src/util/date_fns_locales.ts +168 -0
- package/src/util/entities.ts +16 -1
- package/src/util/entity_cache.ts +18 -0
- package/src/util/geopoint.ts +81 -0
- package/src/util/index.ts +3 -0
- package/src/util/lazy_eager.tsx +30 -8
- package/src/util/navigation_blocking.ts +45 -0
- package/src/util/navigation_from_path.ts +47 -7
- package/src/util/navigation_utils.ts +250 -5
- package/src/util/parent_references_from_path.ts +45 -4
- package/src/util/paths.ts +40 -3
- package/src/util/permissions.ts +22 -10
- package/src/util/previews.ts +8 -1
- package/src/util/resolutions.ts +8 -0
- package/src/util/urls.ts +52 -0
- package/src/util/useStorageUploadController.tsx +6 -1
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jest-environment jsdom
|
|
3
|
+
*/
|
|
4
|
+
import React from "react";
|
|
5
|
+
import { render, screen } from "@testing-library/react";
|
|
6
|
+
import { useTranslation } from "react-i18next";
|
|
7
|
+
import { FireCMSi18nProvider } from "../FireCMSi18nProvider";
|
|
8
|
+
|
|
9
|
+
/** Reads a key through the same channel application code uses. */
|
|
10
|
+
function Str({ k }: { k: string }) {
|
|
11
|
+
const { t } = useTranslation("firecms_core");
|
|
12
|
+
return <span data-testid={k}>{t(k)}</span>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const HOST_TRANSLATIONS = {
|
|
16
|
+
en: {
|
|
17
|
+
host_only_key: "Explore your Firestore data",
|
|
18
|
+
save: "Publish" // an override of a real core key
|
|
19
|
+
}
|
|
20
|
+
} as any;
|
|
21
|
+
|
|
22
|
+
describe("FireCMSi18nProvider", () => {
|
|
23
|
+
|
|
24
|
+
beforeEach(() => {
|
|
25
|
+
window.localStorage.clear();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it("resolves its own translations", () => {
|
|
29
|
+
render(
|
|
30
|
+
<FireCMSi18nProvider translations={HOST_TRANSLATIONS}>
|
|
31
|
+
<Str k={"host_only_key"}/>
|
|
32
|
+
</FireCMSi18nProvider>
|
|
33
|
+
);
|
|
34
|
+
expect(screen.getByTestId("host_only_key").textContent).toBe("Explore your Firestore data");
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("keeps the host's translations when a nested provider is mounted without any", () => {
|
|
38
|
+
// This is the FireCMSCloudApp case: it mounts its own provider with
|
|
39
|
+
// `translations={appConfig?.translations}`, which is undefined for every
|
|
40
|
+
// project that has not deployed custom code. A nested provider that built its
|
|
41
|
+
// own isolated instance would shadow the host's and render the raw key.
|
|
42
|
+
render(
|
|
43
|
+
<FireCMSi18nProvider translations={HOST_TRANSLATIONS}>
|
|
44
|
+
<FireCMSi18nProvider translations={undefined}>
|
|
45
|
+
<Str k={"host_only_key"}/>
|
|
46
|
+
</FireCMSi18nProvider>
|
|
47
|
+
</FireCMSi18nProvider>
|
|
48
|
+
);
|
|
49
|
+
expect(screen.getByTestId("host_only_key").textContent).toBe("Explore your Firestore data");
|
|
50
|
+
expect(screen.getByTestId("host_only_key").textContent).not.toBe("host_only_key");
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("does not let a nested provider clobber a host override", () => {
|
|
54
|
+
render(
|
|
55
|
+
<FireCMSi18nProvider translations={HOST_TRANSLATIONS}>
|
|
56
|
+
<FireCMSi18nProvider translations={undefined}>
|
|
57
|
+
<Str k={"save"}/>
|
|
58
|
+
</FireCMSi18nProvider>
|
|
59
|
+
</FireCMSi18nProvider>
|
|
60
|
+
);
|
|
61
|
+
expect(screen.getByTestId("save").textContent).toBe("Publish");
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it("lets a nested provider add its own translations on top of the host's", () => {
|
|
65
|
+
render(
|
|
66
|
+
<FireCMSi18nProvider translations={HOST_TRANSLATIONS}>
|
|
67
|
+
<FireCMSi18nProvider translations={{ en: { nested_key: "From the nested provider" } } as any}>
|
|
68
|
+
<>
|
|
69
|
+
<Str k={"host_only_key"}/>
|
|
70
|
+
<Str k={"nested_key"}/>
|
|
71
|
+
</>
|
|
72
|
+
</FireCMSi18nProvider>
|
|
73
|
+
</FireCMSi18nProvider>
|
|
74
|
+
);
|
|
75
|
+
expect(screen.getByTestId("host_only_key").textContent).toBe("Explore your Firestore data");
|
|
76
|
+
expect(screen.getByTestId("nested_key").textContent).toBe("From the nested provider");
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it("still serves the built-in core strings through a nested provider", () => {
|
|
80
|
+
render(
|
|
81
|
+
<FireCMSi18nProvider>
|
|
82
|
+
<FireCMSi18nProvider translations={undefined}>
|
|
83
|
+
<Str k={"discard"}/>
|
|
84
|
+
</FireCMSi18nProvider>
|
|
85
|
+
</FireCMSi18nProvider>
|
|
86
|
+
);
|
|
87
|
+
const el = screen.getByTestId("discard");
|
|
88
|
+
expect(el.textContent).not.toBe("discard");
|
|
89
|
+
expect(el.textContent?.length).toBeGreaterThan(0);
|
|
90
|
+
});
|
|
91
|
+
});
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jest-environment jsdom
|
|
3
|
+
*/
|
|
4
|
+
import React from "react";
|
|
5
|
+
import { render, screen } from "@testing-library/react";
|
|
6
|
+
import { useTranslation } from "react-i18next";
|
|
7
|
+
import { FireCMSi18nProvider } from "../FireCMSi18nProvider";
|
|
8
|
+
|
|
9
|
+
function Str({ k }: { k: string }) {
|
|
10
|
+
const { t } = useTranslation("firecms_core");
|
|
11
|
+
return <span data-testid={k}>{t(k)}</span>;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const host = { en: { host_key: "Host" } } as any;
|
|
15
|
+
const projectA = { en: { only_in_a: "From project A" } } as any;
|
|
16
|
+
const projectB = { en: { only_in_b: "From project B" } } as any;
|
|
17
|
+
|
|
18
|
+
describe("nested provider lifecycle", () => {
|
|
19
|
+
beforeEach(() => window.localStorage.clear());
|
|
20
|
+
|
|
21
|
+
it("does not carry one project's translations into the next", () => {
|
|
22
|
+
// The real shape of a project switch: App.tsx's provider stays mounted while
|
|
23
|
+
// FireCMSCloudApp's inner one is torn down and rebuilt with another project's
|
|
24
|
+
// translations. Both share the outer i18next instance now, so anything the
|
|
25
|
+
// first project added could outlive it.
|
|
26
|
+
const { rerender } = render(
|
|
27
|
+
<FireCMSi18nProvider translations={host}>
|
|
28
|
+
<FireCMSi18nProvider key={"a"} translations={projectA}>
|
|
29
|
+
<Str k={"only_in_a"}/>
|
|
30
|
+
</FireCMSi18nProvider>
|
|
31
|
+
</FireCMSi18nProvider>
|
|
32
|
+
);
|
|
33
|
+
expect(screen.getByTestId("only_in_a").textContent).toBe("From project A");
|
|
34
|
+
|
|
35
|
+
rerender(
|
|
36
|
+
<FireCMSi18nProvider translations={host}>
|
|
37
|
+
<FireCMSi18nProvider key={"b"} translations={projectB}>
|
|
38
|
+
<>
|
|
39
|
+
<Str k={"only_in_b"}/>
|
|
40
|
+
<Str k={"only_in_a"}/>
|
|
41
|
+
</>
|
|
42
|
+
</FireCMSi18nProvider>
|
|
43
|
+
</FireCMSi18nProvider>
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
expect(screen.getByTestId("only_in_b").textContent).toBe("From project B");
|
|
47
|
+
expect(screen.getByTestId("only_in_a").textContent).toBe("only_in_a");
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it("keeps the host's own translations across the switch", () => {
|
|
51
|
+
const { rerender } = render(
|
|
52
|
+
<FireCMSi18nProvider translations={host}>
|
|
53
|
+
<FireCMSi18nProvider key={"a"} translations={projectA}>
|
|
54
|
+
<Str k={"host_key"}/>
|
|
55
|
+
</FireCMSi18nProvider>
|
|
56
|
+
</FireCMSi18nProvider>
|
|
57
|
+
);
|
|
58
|
+
rerender(
|
|
59
|
+
<FireCMSi18nProvider translations={host}>
|
|
60
|
+
<FireCMSi18nProvider key={"b"} translations={projectB}>
|
|
61
|
+
<Str k={"host_key"}/>
|
|
62
|
+
</FireCMSi18nProvider>
|
|
63
|
+
</FireCMSi18nProvider>
|
|
64
|
+
);
|
|
65
|
+
expect(screen.getByTestId("host_key").textContent).toBe("Host");
|
|
66
|
+
});
|
|
67
|
+
});
|
|
@@ -55,6 +55,7 @@ export function useBuildDataSource({
|
|
|
55
55
|
*/
|
|
56
56
|
fetchCollection: useCallback(<M extends Record<string, any>>({
|
|
57
57
|
path,
|
|
58
|
+
pathSegments,
|
|
58
59
|
collection,
|
|
59
60
|
filter,
|
|
60
61
|
limit,
|
|
@@ -67,6 +68,7 @@ export function useBuildDataSource({
|
|
|
67
68
|
const usedDelegate = collection?.overrides?.dataSourceDelegate ?? delegate;
|
|
68
69
|
return usedDelegate.fetchCollection<M>({
|
|
69
70
|
path,
|
|
71
|
+
pathSegments,
|
|
70
72
|
filter,
|
|
71
73
|
limit,
|
|
72
74
|
startAfter,
|
|
@@ -97,6 +99,7 @@ export function useBuildDataSource({
|
|
|
97
99
|
? useCallback(<M extends Record<string, any>>(
|
|
98
100
|
{
|
|
99
101
|
path,
|
|
102
|
+
pathSegments,
|
|
100
103
|
collection: collectionProp,
|
|
101
104
|
filter,
|
|
102
105
|
limit,
|
|
@@ -109,7 +112,7 @@ export function useBuildDataSource({
|
|
|
109
112
|
}: ListenCollectionProps<M>
|
|
110
113
|
): () => void => {
|
|
111
114
|
|
|
112
|
-
const collection = collectionProp ?? navigationController.getCollection(path);
|
|
115
|
+
const collection = collectionProp ?? navigationController.getCollection(path, false, pathSegments);
|
|
113
116
|
const usedDelegate = collection?.overrides?.dataSourceDelegate ?? delegate;
|
|
114
117
|
|
|
115
118
|
if (!usedDelegate.listenCollection)
|
|
@@ -117,6 +120,7 @@ export function useBuildDataSource({
|
|
|
117
120
|
|
|
118
121
|
return usedDelegate.listenCollection<M>({
|
|
119
122
|
path,
|
|
123
|
+
pathSegments,
|
|
120
124
|
filter,
|
|
121
125
|
limit,
|
|
122
126
|
startAfter,
|
|
@@ -139,6 +143,7 @@ export function useBuildDataSource({
|
|
|
139
143
|
*/
|
|
140
144
|
fetchEntity: useCallback(<M extends Record<string, any>>({
|
|
141
145
|
path,
|
|
146
|
+
pathSegments,
|
|
142
147
|
entityId,
|
|
143
148
|
collection
|
|
144
149
|
}: FetchEntityProps<M>
|
|
@@ -146,6 +151,7 @@ export function useBuildDataSource({
|
|
|
146
151
|
const usedDelegate = collection?.overrides?.dataSourceDelegate ?? delegate;
|
|
147
152
|
return usedDelegate.fetchEntity({
|
|
148
153
|
path,
|
|
154
|
+
pathSegments,
|
|
149
155
|
entityId,
|
|
150
156
|
collection
|
|
151
157
|
});
|
|
@@ -165,6 +171,7 @@ export function useBuildDataSource({
|
|
|
165
171
|
? useCallback(<M extends Record<string, any>>(
|
|
166
172
|
{
|
|
167
173
|
path,
|
|
174
|
+
pathSegments,
|
|
168
175
|
entityId,
|
|
169
176
|
collection,
|
|
170
177
|
onUpdate,
|
|
@@ -177,6 +184,7 @@ export function useBuildDataSource({
|
|
|
177
184
|
|
|
178
185
|
return usedDelegate.listenEntity<M>({
|
|
179
186
|
path,
|
|
187
|
+
pathSegments,
|
|
180
188
|
entityId,
|
|
181
189
|
onUpdate,
|
|
182
190
|
onError,
|
|
@@ -198,13 +206,14 @@ export function useBuildDataSource({
|
|
|
198
206
|
saveEntity: useCallback(async <M extends Record<string, any>>(
|
|
199
207
|
{
|
|
200
208
|
path,
|
|
209
|
+
pathSegments,
|
|
201
210
|
entityId,
|
|
202
211
|
values,
|
|
203
212
|
collection: collectionProp,
|
|
204
213
|
status
|
|
205
214
|
}: SaveEntityProps<M>): Promise<Entity<M>> => {
|
|
206
215
|
|
|
207
|
-
const collection = collectionProp ?? navigationController.getCollection(path);
|
|
216
|
+
const collection = collectionProp ?? navigationController.getCollection(path, false, pathSegments);
|
|
208
217
|
const usedDelegate = collection?.overrides?.dataSourceDelegate ?? delegate;
|
|
209
218
|
|
|
210
219
|
const resolvedCollection = collection
|
|
@@ -244,6 +253,7 @@ export function useBuildDataSource({
|
|
|
244
253
|
try {
|
|
245
254
|
const entities = await usedDelegate.fetchCollection({
|
|
246
255
|
path,
|
|
256
|
+
pathSegments,
|
|
247
257
|
orderBy: orderProperty,
|
|
248
258
|
order: "asc",
|
|
249
259
|
limit: 1,
|
|
@@ -270,14 +280,21 @@ export function useBuildDataSource({
|
|
|
270
280
|
|
|
271
281
|
return usedDelegate.saveEntity({
|
|
272
282
|
path,
|
|
283
|
+
pathSegments,
|
|
273
284
|
collection,
|
|
274
285
|
entityId,
|
|
275
286
|
values: finalValues,
|
|
276
287
|
status
|
|
277
288
|
}).then((res) => {
|
|
289
|
+
// Keep everything the delegate resolved — `databaseId`, and `pathSegments` in
|
|
290
|
+
// particular — so a just-saved entity is as resolvable as a fetched one.
|
|
291
|
+
// Only the values are re-mapped back to the CMS model.
|
|
278
292
|
return {
|
|
279
|
-
|
|
280
|
-
|
|
293
|
+
...res,
|
|
294
|
+
// A delegate that predates `pathSegments` returns none; the segments we
|
|
295
|
+
// just saved with describe exactly this entity's collection, so they are
|
|
296
|
+
// the authoritative answer rather than a guess.
|
|
297
|
+
pathSegments: res.pathSegments ?? pathSegments,
|
|
281
298
|
values: usedDelegate.delegateToCMSModel(finalValues)
|
|
282
299
|
} as Entity<M>;
|
|
283
300
|
});
|
|
@@ -292,12 +309,15 @@ export function useBuildDataSource({
|
|
|
292
309
|
deleteEntity: useCallback(<M extends Record<string, any>>(
|
|
293
310
|
{
|
|
294
311
|
entity,
|
|
312
|
+
pathSegments,
|
|
295
313
|
collection
|
|
296
314
|
}: DeleteEntityProps<M>
|
|
297
315
|
): Promise<void> => {
|
|
298
316
|
const usedDelegate = collection?.overrides?.dataSourceDelegate ?? delegate;
|
|
299
317
|
return usedDelegate.deleteEntity({
|
|
300
318
|
entity,
|
|
319
|
+
// Falls back to what the entity carries from the fetch that produced it.
|
|
320
|
+
pathSegments: pathSegments ?? entity.pathSegments,
|
|
301
321
|
collection
|
|
302
322
|
});
|
|
303
323
|
}, [delegate.deleteEntity]),
|
|
@@ -317,33 +337,32 @@ export function useBuildDataSource({
|
|
|
317
337
|
name: string,
|
|
318
338
|
value: any,
|
|
319
339
|
entityId?: string,
|
|
320
|
-
collection?: EntityCollection
|
|
340
|
+
collection?: EntityCollection,
|
|
341
|
+
pathSegments?: string[]
|
|
321
342
|
): Promise<boolean> => {
|
|
322
343
|
const usedDelegate = collection?.overrides?.dataSourceDelegate ?? delegate;
|
|
323
|
-
return usedDelegate.checkUniqueField(path, name, value, entityId, collection);
|
|
344
|
+
return usedDelegate.checkUniqueField(path, name, value, entityId, collection, pathSegments);
|
|
324
345
|
}, [delegate.checkUniqueField]),
|
|
325
346
|
|
|
326
|
-
generateEntityId: useCallback((path: string, collection: EntityCollection): string => {
|
|
347
|
+
generateEntityId: useCallback((path: string, collection: EntityCollection, pathSegments?: string[]): string => {
|
|
327
348
|
const usedDelegate = collection?.overrides?.dataSourceDelegate ?? delegate;
|
|
328
|
-
return usedDelegate.generateEntityId(path, collection);
|
|
349
|
+
return usedDelegate.generateEntityId(path, collection, pathSegments);
|
|
329
350
|
}, [delegate.generateEntityId]),
|
|
330
351
|
|
|
352
|
+
// Typed with the declared props rather than an inline duplicate: the duplicate is
|
|
353
|
+
// free to drift from the interface, and a field it forgets is dropped in silence.
|
|
331
354
|
countEntities: delegate.countEntities ? async ({
|
|
332
355
|
path,
|
|
356
|
+
pathSegments,
|
|
333
357
|
collection,
|
|
334
358
|
filter,
|
|
335
359
|
order,
|
|
336
360
|
orderBy
|
|
337
|
-
}: {
|
|
338
|
-
path: string,
|
|
339
|
-
collection: EntityCollection<any>,
|
|
340
|
-
filter?: FilterValues<Extract<keyof any, string>>,
|
|
341
|
-
orderBy?: string,
|
|
342
|
-
order?: "desc" | "asc",
|
|
343
|
-
}): Promise<number> => {
|
|
361
|
+
}: FetchCollectionProps<any>): Promise<number> => {
|
|
344
362
|
const usedDelegate = collection?.overrides?.dataSourceDelegate ?? delegate;
|
|
345
363
|
return usedDelegate.countEntities!({
|
|
346
364
|
path,
|
|
365
|
+
pathSegments,
|
|
347
366
|
filter,
|
|
348
367
|
orderBy,
|
|
349
368
|
order,
|
|
@@ -353,11 +372,13 @@ export function useBuildDataSource({
|
|
|
353
372
|
|
|
354
373
|
isFilterCombinationValid: useCallback(({
|
|
355
374
|
path,
|
|
375
|
+
pathSegments,
|
|
356
376
|
databaseId,
|
|
357
377
|
filterValues,
|
|
358
378
|
sortBy
|
|
359
379
|
}: {
|
|
360
380
|
path: string,
|
|
381
|
+
pathSegments?: string[],
|
|
361
382
|
databaseId?: string,
|
|
362
383
|
filterValues: FilterValues<any>,
|
|
363
384
|
sortBy?: [string, "asc" | "desc"]
|
|
@@ -367,6 +388,7 @@ export function useBuildDataSource({
|
|
|
367
388
|
return delegate.isFilterCombinationValid(
|
|
368
389
|
{
|
|
369
390
|
path,
|
|
391
|
+
pathSegments,
|
|
370
392
|
databaseId,
|
|
371
393
|
filterValues,
|
|
372
394
|
sortBy
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
import { getNavigationEntriesFromPath, NavigationViewInternal } from "../util/navigation_from_path";
|
|
14
14
|
import { useLocation } from "react-router-dom";
|
|
15
15
|
import {
|
|
16
|
+
encodeEntityId,
|
|
16
17
|
removeInitialAndTrailingSlashes,
|
|
17
18
|
resolveCollection,
|
|
18
19
|
resolveDefaultSelectedView,
|
|
@@ -223,12 +224,14 @@ export function buildSidePanelsFromUrl(path: string, collections: EntityCollecti
|
|
|
223
224
|
let sidePanel: EntitySidePanelProps<any> | undefined = undefined;
|
|
224
225
|
let lastCollectionPath = "";
|
|
225
226
|
let lastCollectionId: string | undefined = undefined;
|
|
227
|
+
let lastCollectionSegments: string[] | undefined = undefined;
|
|
226
228
|
for (let i = 0; i < navigationViewsForPath.length; i++) {
|
|
227
229
|
const navigationEntry = navigationViewsForPath[i];
|
|
228
230
|
|
|
229
231
|
if (navigationEntry.type === "collection") {
|
|
230
232
|
lastCollectionPath = navigationEntry.path;
|
|
231
233
|
lastCollectionId = navigationEntry.collection.id;
|
|
234
|
+
lastCollectionSegments = navigationEntry.pathSegments;
|
|
232
235
|
}
|
|
233
236
|
|
|
234
237
|
const previousEntry = navigationViewsForPath[i - 1];
|
|
@@ -236,6 +239,7 @@ export function buildSidePanelsFromUrl(path: string, collections: EntityCollecti
|
|
|
236
239
|
sidePanel = {
|
|
237
240
|
path: navigationEntry.path,
|
|
238
241
|
fullIdPath: navigationEntry.fullIdPath,
|
|
242
|
+
pathSegments: navigationEntry.pathSegments,
|
|
239
243
|
entityId: navigationEntry.entityId,
|
|
240
244
|
copy: false,
|
|
241
245
|
width: navigationEntry.parentCollection?.sideDialogWidth
|
|
@@ -258,6 +262,9 @@ export function buildSidePanelsFromUrl(path: string, collections: EntityCollecti
|
|
|
258
262
|
sidePanel = {
|
|
259
263
|
path: lastCollectionPath,
|
|
260
264
|
fullIdPath: lastCollectionId,
|
|
265
|
+
// A new entity still needs unambiguous segments: it may be created inside a
|
|
266
|
+
// subcollection whose parent id contains "/".
|
|
267
|
+
pathSegments: lastCollectionSegments,
|
|
261
268
|
copy: false
|
|
262
269
|
}
|
|
263
270
|
}
|
|
@@ -274,11 +281,17 @@ const propsToSidePanel = (props: EntitySidePanelProps,
|
|
|
274
281
|
locationSearch: string
|
|
275
282
|
): SideDialogPanelProps => {
|
|
276
283
|
|
|
277
|
-
|
|
284
|
+
// The URL is the ESCAPED representation of the chain — it is what `buildSidePanelsFromUrl`
|
|
285
|
+
// and `FireCMSRoute` parse back on reload, on a deep link, and on every pathname change.
|
|
286
|
+
// `props.path` is the RAW datasource path, so a parent entity id containing "/" reads back
|
|
287
|
+
// there as extra collection/entity hops and resolves to a different entity entirely.
|
|
288
|
+
// `fullIdPath` carries the same chain with its ids already escaped, which is what it is
|
|
289
|
+
// threaded alongside `path` for.
|
|
290
|
+
const urlChain = removeInitialAndTrailingSlashes(props.fullIdPath ?? props.path);
|
|
278
291
|
|
|
279
292
|
const urlPath = props.entityId
|
|
280
|
-
? buildUrlCollectionPath(`${
|
|
281
|
-
: buildUrlCollectionPath(`${
|
|
293
|
+
? buildUrlCollectionPath(`${urlChain}/${encodeEntityId(props.entityId)}${props.selectedTab ? "/" + props.selectedTab : ""}${locationSearch}#${SIDE_URL_HASH}`)
|
|
294
|
+
: buildUrlCollectionPath(`${urlChain}${locationSearch}#${NEW_URL_HASH}`);
|
|
282
295
|
|
|
283
296
|
const resolvedPanelProps: EntitySidePanelProps<any> = {
|
|
284
297
|
...props,
|
|
@@ -287,10 +300,12 @@ const propsToSidePanel = (props: EntitySidePanelProps,
|
|
|
287
300
|
|
|
288
301
|
const entityViewWidth = getEntityViewWidth(props, smallLayout, customizationController, authController);
|
|
289
302
|
return {
|
|
290
|
-
|
|
303
|
+
// Built from the escaped chain so that two different chains which happen to flatten
|
|
304
|
+
// to the same raw string are still two different panels.
|
|
305
|
+
key: `${urlChain}/${props.entityId ? encodeEntityId(props.entityId) : ""}`,
|
|
291
306
|
component: undefined, // Lazy render in SideDialogs for better performance
|
|
292
307
|
urlPath: urlPath,
|
|
293
|
-
parentUrlPath: buildUrlCollectionPath(
|
|
308
|
+
parentUrlPath: buildUrlCollectionPath(urlChain),
|
|
294
309
|
width: entityViewWidth,
|
|
295
310
|
onClose: props.onClose,
|
|
296
311
|
additional: resolvedPanelProps
|
package/src/locales/de.ts
CHANGED
|
@@ -65,8 +65,19 @@ export const de: FireCMSTranslations = {
|
|
|
65
65
|
select_from: "Aus {{name}} auswählen",
|
|
66
66
|
done: "Fertig",
|
|
67
67
|
log_out: "Abmelden",
|
|
68
|
-
|
|
69
|
-
|
|
68
|
+
license_trial_banner: "FireCMS PRO-Testphase: noch {{days}} Tage in Produktion.",
|
|
69
|
+
license_trial_banner_one: "FireCMS PRO-Testphase: noch {{days}} Tag in Produktion.",
|
|
70
|
+
license_expired_banner: "Ihre PRO-Testphase ist am {{date}} abgelaufen. PRO-Funktionen wie Schema-Editor, Import/Export und Verlauf sind pausiert; Ihre Daten und Sammlungen funktionieren weiterhin.",
|
|
71
|
+
license_expired_banner_undated: "Ihre PRO-Testphase ist abgelaufen. PRO-Funktionen wie Schema-Editor, Import/Export und Verlauf sind pausiert; Ihre Daten und Sammlungen funktionieren weiterhin.",
|
|
72
|
+
license_invalid_project_banner: "Dieser Lizenzschlüssel ist nicht mit dem Projekt {{projectId}} verknüpft. Bis dahin sind die PRO-Funktionen pausiert.",
|
|
73
|
+
license_invalid_project_banner_unnamed: "Dieser Lizenzschlüssel ist nicht mit diesem Projekt verknüpft. Bis dahin sind die PRO-Funktionen pausiert.",
|
|
74
|
+
license_over_quota_banner: "Die Lizenz dieses Projekts deckt {{licensed}} Projekte in Produktion ab, und dieses gehört nicht dazu. PRO-Funktionen wie Schema-Editor, Import/Export und Verlauf sind pausiert; Ihre Daten und Sammlungen funktionieren weiterhin.",
|
|
75
|
+
license_over_quota_banner_one: "Die Lizenz dieses Projekts deckt {{licensed}} Projekt in Produktion ab, und das ist ein anderes. PRO-Funktionen wie Schema-Editor, Import/Export und Verlauf sind pausiert; Ihre Daten und Sammlungen funktionieren weiterhin.",
|
|
76
|
+
license_get_license: "Lizenz erwerben",
|
|
77
|
+
license_update_license: "Lizenz aktualisieren",
|
|
78
|
+
license_add_project_to_license: "Zu Ihrer Lizenz hinzufügen",
|
|
79
|
+
pro_features_paused: "PRO-Funktionen pausiert",
|
|
80
|
+
user_management_paused_description: "Die Verwaltung von Benutzern und Rollen ist Teil von FireCMS PRO. Ihre bestehenden Benutzer und Rollen legen weiterhin fest, wer sich anmelden darf und was er tun kann. Bearbeiten können Sie sie wieder, sobald dieses Projekt eine Lizenz hat.",
|
|
70
81
|
column_cannot_be_edited: "Diese Spalte kann nicht direkt bearbeitet werden",
|
|
71
82
|
close: "Schließen",
|
|
72
83
|
unsaved_local_changes: "Ungespeicherte lokale Änderungen",
|
|
@@ -146,8 +157,8 @@ export const de: FireCMSTranslations = {
|
|
|
146
157
|
// ─── Error states ─────────────────────────────────────────────
|
|
147
158
|
error: "Fehler",
|
|
148
159
|
error_uploading_file: "Fehler beim Hochladen der Datei",
|
|
149
|
-
error_deleting: "Fehler beim Löschen",
|
|
150
|
-
error_before_delete: "Fehler vor dem Löschen",
|
|
160
|
+
error_deleting: "Fehler beim Löschen: {{message}}",
|
|
161
|
+
error_before_delete: "Fehler vor dem Löschen: {{message}}",
|
|
151
162
|
error_updating_asset: "Fehler beim Aktualisieren des Assets",
|
|
152
163
|
error_deleting_asset: "Fehler beim Löschen des Assets",
|
|
153
164
|
error_firestore_index: "Für diese Abfrage ist ein Firestore-Index erforderlich.",
|
|
@@ -389,6 +400,10 @@ export const de: FireCMSTranslations = {
|
|
|
389
400
|
download: "Herunterladen",
|
|
390
401
|
large_number_of_documents: "Diese Sammlung hat eine große Anzahl von Dokumenten ({{count}}).",
|
|
391
402
|
include_undefined_values: "Undefinierte Werte einschließen",
|
|
403
|
+
export_apply_filter_sort: "Nur Ergebnisse exportieren, die dem aktuellen Filter/der aktuellen Sortierung entsprechen",
|
|
404
|
+
export_all_entities: "Alle Entitäten",
|
|
405
|
+
export_selected_entities: "Ausgewählte Entitäten ({{count}})",
|
|
406
|
+
export_error: "Die Daten konnten nicht exportiert werden: {{message}}",
|
|
392
407
|
submit: "Einreichen",
|
|
393
408
|
|
|
394
409
|
no_filterable_properties: "Keine filterbaren Eigenschaften verfügbar",
|
|
@@ -438,6 +453,7 @@ export const de: FireCMSTranslations = {
|
|
|
438
453
|
roles_menu: "Rollen",
|
|
439
454
|
project_settings: "Projekteinstellungen",
|
|
440
455
|
firestore_manager: "Firestore-Manager",
|
|
456
|
+
datatalk: "DataTalk",
|
|
441
457
|
manage_your_firestore_data: "Deine Firestore-Daten verwalten",
|
|
442
458
|
|
|
443
459
|
// ─── FireCMS Cloud Login ──────────────────────────────────────
|
|
@@ -454,6 +470,7 @@ export const de: FireCMSTranslations = {
|
|
|
454
470
|
not_the_admin_panel: "nicht auf das Admin-Panel.",
|
|
455
471
|
join_our_newsletter: "Melden Sie sich für unseren Newsletter an. Kein Spam, nur wichtige Updates!",
|
|
456
472
|
by_signing_in_you_agree_to_our: "Mit der Anmeldung stimmen Sie unseren",
|
|
473
|
+
please_accept_the_terms_to_continue: "Bitte akzeptieren Sie die Nutzungsbedingungen, um fortzufahren",
|
|
457
474
|
terms_and_conditions: "Allgemeinen Geschäftsbedingungen",
|
|
458
475
|
and_our: "und unserer",
|
|
459
476
|
privacy_policy: "Datenschutzerklärung",
|
|
@@ -496,6 +513,7 @@ export const de: FireCMSTranslations = {
|
|
|
496
513
|
licensed_projects_lowercase: "lizenzierte projekte",
|
|
497
514
|
manage: "Verwalten",
|
|
498
515
|
update_payment_method: "Zahlungsmethode aktualisieren",
|
|
516
|
+
stripe_portal_error: "Stripe konnte nicht geöffnet werden: {{message}}",
|
|
499
517
|
your_firecms_cloud_projects: "Ihre FireCMS Cloud Projekte",
|
|
500
518
|
status_active: "Aktiv",
|
|
501
519
|
status_trialing: "Testphase",
|
|
@@ -586,6 +604,10 @@ export const de: FireCMSTranslations = {
|
|
|
586
604
|
order_property_not_found: "Eigenschaft \"{{property}}\" existiert nicht oder ist keine Zahlen-Eigenschaft. Bitte wählen Sie eine gültige Eigenschaft oder löschen Sie die Auswahl.",
|
|
587
605
|
no_number_properties: "Keine Zahlen-Eigenschaften gefunden. Fügen Sie eine Zahlen-Eigenschaft hinzu, um die Sortierung zu aktivieren.",
|
|
588
606
|
order_property_description: "Wählen Sie eine Zahlen-Eigenschaft, um die Reihenfolge der Elemente beizubehalten",
|
|
607
|
+
image_property: "Bild-Eigenschaft",
|
|
608
|
+
image_property_not_found: "Eigenschaft \"{{property}}\" existiert nicht oder ist keine Bild-Eigenschaft. Bitte wählen Sie eine gültige Eigenschaft oder löschen Sie die Auswahl.",
|
|
609
|
+
no_image_properties: "Keine Bild-Eigenschaften gefunden. Fügen Sie eine Speicher- oder Bild-URL-Eigenschaft hinzu, um sie als Vorschaubild zu verwenden.",
|
|
610
|
+
image_property_description: "Wählen Sie die Eigenschaft, die als Vorschaubild in den Ansichten Karten und Kanban verwendet wird. Wenn nicht festgelegt, wird automatisch die erste passende Bild-Eigenschaft verwendet.",
|
|
589
611
|
display_settings: "Anzeigeeinstellungen",
|
|
590
612
|
default_row_size: "Standard-Zeilengröße",
|
|
591
613
|
side_dialog_width: "Seitendialog-Breite",
|
package/src/locales/en.ts
CHANGED
|
@@ -73,8 +73,19 @@ export const en: FireCMSTranslations = {
|
|
|
73
73
|
select_from: "Select from {{name}}",
|
|
74
74
|
done: "Done",
|
|
75
75
|
log_out: "Log out",
|
|
76
|
-
|
|
77
|
-
|
|
76
|
+
license_trial_banner: "FireCMS PRO trial: {{days}} days left in production.",
|
|
77
|
+
license_trial_banner_one: "FireCMS PRO trial: {{days}} day left in production.",
|
|
78
|
+
license_expired_banner: "Your PRO trial ended on {{date}}. PRO features such as the schema editor, import/export and history are paused; your data and collections still work.",
|
|
79
|
+
license_expired_banner_undated: "Your PRO trial has ended. PRO features such as the schema editor, import/export and history are paused; your data and collections still work.",
|
|
80
|
+
license_invalid_project_banner: "This license key is not linked to project {{projectId}}. PRO features are paused until it is.",
|
|
81
|
+
license_invalid_project_banner_unnamed: "This license key is not linked to this project. PRO features are paused until it is.",
|
|
82
|
+
license_over_quota_banner: "This project's license pays for {{licensed}} projects in production, and this is not one of them. PRO features such as the schema editor, import/export and history are paused; your data and collections still work.",
|
|
83
|
+
license_over_quota_banner_one: "This project's license pays for {{licensed}} project in production, and it is a different one. PRO features such as the schema editor, import/export and history are paused; your data and collections still work.",
|
|
84
|
+
license_get_license: "Get a license",
|
|
85
|
+
license_update_license: "Update your license",
|
|
86
|
+
license_add_project_to_license: "Add it to your license",
|
|
87
|
+
pro_features_paused: "PRO features paused",
|
|
88
|
+
user_management_paused_description: "Managing users and roles is part of FireCMS PRO. Your existing users and roles still decide who can sign in and what they can do, and you can edit them again once this project has a license.",
|
|
78
89
|
column_cannot_be_edited: "This column can't be edited directly",
|
|
79
90
|
close: "Close",
|
|
80
91
|
unsaved_local_changes: "Unsaved Local changes",
|
|
@@ -154,8 +165,8 @@ export const en: FireCMSTranslations = {
|
|
|
154
165
|
// ─── Error states ─────────────────────────────────────────────
|
|
155
166
|
error: "Error",
|
|
156
167
|
error_uploading_file: "Error uploading file",
|
|
157
|
-
error_deleting: "Error deleting",
|
|
158
|
-
error_before_delete: "Error before delete",
|
|
168
|
+
error_deleting: "Error deleting: {{message}}",
|
|
169
|
+
error_before_delete: "Error before delete: {{message}}",
|
|
159
170
|
error_updating_asset: "Error updating asset",
|
|
160
171
|
error_deleting_asset: "Error deleting asset",
|
|
161
172
|
error_firestore_index: "A Firestore index is required for this query.",
|
|
@@ -388,15 +399,19 @@ export const en: FireCMSTranslations = {
|
|
|
388
399
|
data_imported_successfully: "Data imported successfully",
|
|
389
400
|
export: "Export",
|
|
390
401
|
export_data: "Export data",
|
|
391
|
-
download_table_csv: "Download the
|
|
402
|
+
download_table_csv: "Download the content of this table as a CSV",
|
|
392
403
|
csv: "CSV",
|
|
393
404
|
json: "JSON",
|
|
394
405
|
dates_as_timestamps: "Dates as timestamps",
|
|
395
406
|
dates_as_strings: "Dates as strings",
|
|
396
407
|
flatten_arrays: "Flatten arrays",
|
|
397
408
|
download: "Download",
|
|
398
|
-
large_number_of_documents: "This
|
|
409
|
+
large_number_of_documents: "This collection has a large number of documents ({{count}}).",
|
|
399
410
|
include_undefined_values: "Include undefined values",
|
|
411
|
+
export_apply_filter_sort: "Only export results matching the current filter/sort",
|
|
412
|
+
export_all_entities: "All entities",
|
|
413
|
+
export_selected_entities: "Selected entities ({{count}})",
|
|
414
|
+
export_error: "Could not export the data: {{message}}",
|
|
400
415
|
submit: "Submit",
|
|
401
416
|
|
|
402
417
|
no_filterable_properties: "No filterable properties available",
|
|
@@ -447,6 +462,9 @@ export const en: FireCMSTranslations = {
|
|
|
447
462
|
project_settings: "Project settings",
|
|
448
463
|
firestore_manager: "Firestore Manager",
|
|
449
464
|
manage_your_firestore_data: "Manage your Firestore data",
|
|
465
|
+
// A product name. It is a key rather than a literal so a locale that uses a
|
|
466
|
+
// different script can transliterate it; most will inherit this value.
|
|
467
|
+
datatalk: "DataTalk",
|
|
450
468
|
|
|
451
469
|
// ─── FireCMS Cloud Login ──────────────────────────────────────
|
|
452
470
|
build_admin_panel_in_minutes: "Build Your Firebase Admin Panel in Minutes",
|
|
@@ -462,6 +480,7 @@ export const en: FireCMSTranslations = {
|
|
|
462
480
|
not_the_admin_panel: "not the admin panel.",
|
|
463
481
|
join_our_newsletter: "Join our newsletter. No spam, only important updates!",
|
|
464
482
|
by_signing_in_you_agree_to_our: "By signing in you agree to our",
|
|
483
|
+
please_accept_the_terms_to_continue: "Please accept the terms and conditions to continue",
|
|
465
484
|
terms_and_conditions: "Terms and Conditions",
|
|
466
485
|
and_our: "and our",
|
|
467
486
|
privacy_policy: "Privacy policy",
|
|
@@ -504,6 +523,7 @@ export const en: FireCMSTranslations = {
|
|
|
504
523
|
licensed_projects_lowercase: "licensed projects",
|
|
505
524
|
manage: "Manage",
|
|
506
525
|
update_payment_method: "Update payment method",
|
|
526
|
+
stripe_portal_error: "Could not open Stripe: {{message}}",
|
|
507
527
|
your_firecms_cloud_projects: "Your FireCMS Cloud projects",
|
|
508
528
|
status_active: "Active",
|
|
509
529
|
status_trialing: "Trialing",
|
|
@@ -598,6 +618,10 @@ export const en: FireCMSTranslations = {
|
|
|
598
618
|
order_property_not_found: "Property \"{{property}}\" does not exist or is not a number property. Please select a valid property or clear the selection.",
|
|
599
619
|
no_number_properties: "No number properties found. Add a number property to enable ordering.",
|
|
600
620
|
order_property_description: "Select a number property to persist the order of items",
|
|
621
|
+
image_property: "Image Property",
|
|
622
|
+
image_property_not_found: "Property \"{{property}}\" does not exist or is not an image property. Please select a valid property or clear the selection.",
|
|
623
|
+
no_image_properties: "No image properties found. Add a storage or URL image property to use as a thumbnail.",
|
|
624
|
+
image_property_description: "Select the property used as the thumbnail image in Cards and Kanban view modes. If not set, the first matching image property will be used automatically.",
|
|
601
625
|
display_settings: "Display settings",
|
|
602
626
|
default_row_size: "Default row size",
|
|
603
627
|
side_dialog_width: "Side dialog width",
|