@firecms/core 3.0.0-beta.2 → 3.0.0-beta.4.pre.1
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 +1 -1
- package/dist/components/EntityCollectionTable/EntityCollectionRowActions.d.ts +3 -7
- package/dist/components/EntityCollectionTable/EntityCollectionTable.d.ts +2 -2
- package/dist/components/EntityCollectionTable/PropertyTableCell.d.ts +2 -2
- package/dist/components/EntityCollectionView/EntityCollectionView.d.ts +1 -2
- package/dist/components/EntityCollectionView/useSelectionController.d.ts +2 -0
- package/dist/components/EntityPreview.d.ts +25 -7
- package/dist/components/EntityView.d.ts +11 -0
- package/dist/components/FieldCaption.d.ts +4 -0
- package/dist/components/HomePage/NavigationCard.d.ts +8 -0
- package/dist/components/HomePage/{NavigationCollectionCard.d.ts → NavigationCardBinding.d.ts} +2 -2
- package/dist/components/HomePage/SmallNavigationCard.d.ts +6 -0
- package/dist/components/HomePage/index.d.ts +3 -1
- package/dist/components/ReferenceWidget.d.ts +3 -3
- package/dist/components/VirtualTable/VirtualTableProps.d.ts +1 -1
- package/dist/components/index.d.ts +4 -3
- package/dist/{internal/EntityView.d.ts → core/EntityEditView.d.ts} +2 -2
- package/dist/core/SideEntityView.d.ts +7 -0
- package/dist/core/index.d.ts +0 -2
- package/dist/form/EntityForm.d.ts +1 -1
- package/dist/form/components/StorageItemPreview.d.ts +3 -2
- package/dist/form/components/StorageUploadProgress.d.ts +1 -1
- package/dist/form/components/index.d.ts +1 -0
- package/dist/form/field_bindings/KeyValueFieldBinding.d.ts +1 -1
- package/dist/form/field_bindings/MapFieldBinding.d.ts +1 -1
- package/dist/form/field_bindings/StorageUploadFieldBinding.d.ts +4 -3
- package/dist/form/field_bindings/TextFieldBinding.d.ts +2 -2
- package/dist/form/index.d.ts +1 -0
- package/dist/form/validation.d.ts +1 -1
- package/dist/hooks/data/useDataSource.d.ts +2 -2
- package/dist/hooks/index.d.ts +2 -1
- package/dist/{core → hooks}/useBuildModeController.d.ts +1 -1
- package/dist/hooks/useBuildNavigationController.d.ts +5 -2
- package/dist/hooks/useProjectLog.d.ts +5 -1
- package/dist/hooks/useStorageSource.d.ts +2 -2
- package/dist/index.es.js +8444 -8152
- 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 +4 -0
- package/dist/internal/useRestoreScroll.d.ts +1 -1
- package/dist/preview/PropertyPreview.d.ts +1 -1
- package/dist/preview/components/BooleanPreview.d.ts +5 -1
- package/dist/preview/components/EnumValuesChip.d.ts +1 -1
- package/dist/preview/components/ReferencePreview.d.ts +1 -7
- package/dist/types/analytics.d.ts +1 -1
- package/dist/types/auth.d.ts +8 -1
- package/dist/types/collections.d.ts +14 -1
- package/dist/types/entities.d.ts +1 -0
- package/dist/types/entity_overrides.d.ts +6 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/navigation.d.ts +10 -9
- package/dist/types/permissions.d.ts +5 -1
- package/dist/types/plugins.d.ts +15 -17
- package/dist/types/properties.d.ts +2 -2
- package/dist/types/property_config.d.ts +2 -2
- package/dist/util/collections.d.ts +9 -1
- package/dist/util/icons.d.ts +8 -2
- package/dist/util/index.d.ts +1 -0
- package/dist/util/permissions.d.ts +4 -4
- package/dist/util/references.d.ts +4 -2
- package/dist/util/resolutions.d.ts +1 -1
- package/package.json +31 -23
- package/src/components/DeleteEntityDialog.tsx +4 -4
- package/src/components/EntityCollectionTable/EntityCollectionRowActions.tsx +35 -39
- package/src/components/EntityCollectionTable/EntityCollectionTable.tsx +276 -282
- package/src/components/EntityCollectionTable/EntityCollectionTableProps.tsx +1 -1
- package/src/components/EntityCollectionTable/PropertyTableCell.tsx +13 -13
- package/src/components/EntityCollectionTable/fields/TableReferenceField.tsx +9 -16
- package/src/components/EntityCollectionTable/fields/TableStorageUpload.tsx +3 -3
- package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +1 -1
- package/src/components/EntityCollectionTable/internal/default_entity_actions.tsx +9 -5
- package/src/components/EntityCollectionView/EntityCollectionView.tsx +37 -58
- package/src/components/EntityCollectionView/EntityCollectionViewActions.tsx +5 -6
- package/src/components/EntityCollectionView/useSelectionController.tsx +30 -0
- package/src/components/EntityPreview.tsx +204 -70
- package/src/components/EntityView.tsx +84 -0
- package/src/components/FieldCaption.tsx +13 -0
- package/src/components/FireCMSAppBar.tsx +8 -0
- package/src/components/HomePage/DefaultHomePage.tsx +13 -9
- package/src/components/HomePage/NavigationCard.tsx +69 -0
- package/src/components/HomePage/NavigationCardBinding.tsx +116 -0
- package/src/components/HomePage/SmallNavigationCard.tsx +45 -0
- package/src/components/HomePage/index.tsx +3 -1
- package/src/components/ReferenceTable/ReferenceSelectionTable.tsx +3 -4
- package/src/components/ReferenceWidget.tsx +7 -7
- package/src/components/SelectableTable/filters/ReferenceFilterField.tsx +11 -19
- package/src/components/VirtualTable/VirtualTableProps.tsx +1 -1
- package/src/components/common/useDataSourceEntityCollectionTableController.tsx +1 -1
- package/src/components/index.tsx +4 -3
- package/src/core/Drawer.tsx +66 -39
- package/src/{internal/EntityView.tsx → core/EntityEditView.tsx} +22 -39
- package/src/core/EntitySidePanel.tsx +2 -2
- package/src/core/FireCMS.tsx +18 -2
- package/src/core/NavigationRoutes.tsx +8 -0
- package/src/core/SideEntityView.tsx +38 -0
- package/src/core/index.tsx +0 -2
- package/src/form/EntityForm.tsx +19 -11
- package/src/form/components/StorageItemPreview.tsx +5 -3
- package/src/form/components/StorageUploadProgress.tsx +6 -5
- package/src/form/components/index.tsx +1 -0
- package/src/form/field_bindings/ArrayCustomShapedFieldBinding.tsx +2 -3
- package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +12 -15
- package/src/form/field_bindings/BlockFieldBinding.tsx +2 -3
- package/src/form/field_bindings/DateTimeFieldBinding.tsx +3 -3
- package/src/form/field_bindings/KeyValueFieldBinding.tsx +18 -18
- package/src/form/field_bindings/MapFieldBinding.tsx +17 -17
- package/src/form/field_bindings/MarkdownFieldBinding.tsx +1 -2
- package/src/form/field_bindings/MultiSelectBinding.tsx +2 -3
- package/src/form/field_bindings/ReadOnlyFieldBinding.tsx +3 -3
- package/src/form/field_bindings/ReferenceFieldBinding.tsx +6 -4
- package/src/form/field_bindings/RepeatFieldBinding.tsx +3 -3
- package/src/form/field_bindings/SelectFieldBinding.tsx +2 -3
- package/src/form/field_bindings/StorageUploadFieldBinding.tsx +15 -6
- package/src/form/field_bindings/SwitchFieldBinding.tsx +2 -3
- package/src/form/field_bindings/TextFieldBinding.tsx +10 -9
- package/src/form/index.tsx +1 -0
- package/src/form/validation.ts +3 -4
- package/src/hooks/data/useCollectionFetch.tsx +1 -1
- package/src/hooks/data/useDataSource.tsx +8 -3
- package/src/hooks/data/useEntityFetch.tsx +1 -1
- package/src/hooks/index.tsx +3 -1
- package/src/{core → hooks}/useBuildModeController.tsx +1 -1
- package/src/hooks/useBuildNavigationController.tsx +105 -49
- package/src/hooks/useProjectLog.tsx +11 -3
- package/src/hooks/useReferenceDialog.tsx +2 -2
- package/src/hooks/useStorageSource.tsx +7 -2
- package/src/internal/useBuildDataSource.ts +7 -2
- package/src/preview/PropertyPreview.tsx +2 -2
- package/src/preview/components/BooleanPreview.tsx +16 -3
- package/src/preview/components/EnumValuesChip.tsx +1 -1
- package/src/preview/components/ReferencePreview.tsx +55 -147
- package/src/preview/property_previews/StringPropertyPreview.tsx +8 -7
- package/src/types/analytics.ts +1 -0
- package/src/types/auth.tsx +11 -1
- package/src/types/collections.ts +16 -1
- package/src/types/entities.ts +4 -0
- package/src/types/entity_actions.tsx +4 -0
- package/src/types/entity_overrides.tsx +7 -0
- package/src/types/firecms.tsx +0 -1
- package/src/types/index.ts +1 -0
- package/src/types/navigation.ts +11 -10
- package/src/types/permissions.ts +6 -1
- package/src/types/plugins.tsx +22 -25
- package/src/types/properties.ts +1 -1
- package/src/types/property_config.tsx +2 -2
- package/src/types/side_entity_controller.tsx +1 -0
- package/src/util/collections.ts +22 -0
- package/src/util/icons.tsx +11 -3
- package/src/util/index.ts +1 -0
- package/src/util/permissions.ts +11 -8
- package/src/util/references.ts +36 -5
- package/src/components/HomePage/NavigationCollectionCard.tsx +0 -146
- /package/dist/{components → form/components}/LabelWithIcon.d.ts +0 -0
- /package/dist/{core → hooks}/useBuildLocalConfigurationPersistence.d.ts +0 -0
- /package/src/{components → form/components}/LabelWithIcon.tsx +0 -0
- /package/src/{core → hooks}/useBuildLocalConfigurationPersistence.tsx +0 -0
|
@@ -3,6 +3,7 @@ import React, { useCallback } from "react";
|
|
|
3
3
|
import {
|
|
4
4
|
ArrayProperty,
|
|
5
5
|
Entity,
|
|
6
|
+
EntityCollection,
|
|
6
7
|
FieldProps,
|
|
7
8
|
ResolvedArrayProperty,
|
|
8
9
|
ResolvedStringProperty,
|
|
@@ -10,11 +11,10 @@ import {
|
|
|
10
11
|
} from "../../types";
|
|
11
12
|
import { useDropzone } from "react-dropzone";
|
|
12
13
|
import { PreviewSize } from "../../preview";
|
|
13
|
-
import { FieldHelperText } from "../components";
|
|
14
|
-
import { LabelWithIcon } from "../../components";
|
|
14
|
+
import { FieldHelperText, LabelWithIcon } from "../components";
|
|
15
15
|
|
|
16
16
|
import { getIconForProperty, isReadOnly } from "../../util";
|
|
17
|
-
import {
|
|
17
|
+
import { useSnackbarController, useStorageSource } from "../../hooks";
|
|
18
18
|
import { DragDropContext, Draggable, Droppable } from "@hello-pangea/dnd";
|
|
19
19
|
import { StorageFieldItem, useStorageUploadController } from "../../util/useStorageUploadController";
|
|
20
20
|
import { StorageUploadProgress } from "../components/StorageUploadProgress";
|
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
focusedMixin,
|
|
28
28
|
Typography
|
|
29
29
|
} from "@firecms/ui";
|
|
30
|
+
import { useClearRestoreValue } from "../useClearRestoreValue";
|
|
30
31
|
|
|
31
32
|
const dropZoneClasses = "box-border relative pt-[2px] items-center border border-transparent min-h-[254px] outline-none rounded-md duration-200 ease-[cubic-bezier(0.4,0,0.2,1)] focus:border-primary-solid";
|
|
32
33
|
const disabledClasses = "border-dotted-gray"
|
|
@@ -55,13 +56,13 @@ export function StorageUploadFieldBinding({
|
|
|
55
56
|
property,
|
|
56
57
|
includeDescription,
|
|
57
58
|
context,
|
|
58
|
-
isSubmitting
|
|
59
|
+
isSubmitting,
|
|
59
60
|
}: StorageUploadFieldProps) {
|
|
60
61
|
|
|
61
62
|
if (!context.entityId)
|
|
62
63
|
throw new Error("StorageUploadFieldBinding: Entity id is null");
|
|
63
64
|
|
|
64
|
-
const storageSource = useStorageSource();
|
|
65
|
+
const storageSource = useStorageSource(context.collection);
|
|
65
66
|
const disabled = isReadOnly(property) || !!property.disabled || isSubmitting;
|
|
66
67
|
|
|
67
68
|
const {
|
|
@@ -108,6 +109,7 @@ export function StorageUploadFieldBinding({
|
|
|
108
109
|
|
|
109
110
|
<StorageUpload
|
|
110
111
|
value={internalValue}
|
|
112
|
+
collection={context.collection}
|
|
111
113
|
name={propertyKey}
|
|
112
114
|
disabled={disabled}
|
|
113
115
|
autoFocus={autoFocus}
|
|
@@ -133,6 +135,7 @@ export function StorageUploadFieldBinding({
|
|
|
133
135
|
|
|
134
136
|
function FileDropComponent({
|
|
135
137
|
storage,
|
|
138
|
+
collection,
|
|
136
139
|
disabled,
|
|
137
140
|
isDraggingOver,
|
|
138
141
|
onFilesAdded,
|
|
@@ -151,6 +154,7 @@ function FileDropComponent({
|
|
|
151
154
|
helpText
|
|
152
155
|
}: {
|
|
153
156
|
storage: StorageConfig,
|
|
157
|
+
collection: EntityCollection,
|
|
154
158
|
disabled: boolean,
|
|
155
159
|
isDraggingOver: boolean,
|
|
156
160
|
droppableProvided: any,
|
|
@@ -231,6 +235,7 @@ function FileDropComponent({
|
|
|
231
235
|
if (entry.storagePathOrDownloadUrl) {
|
|
232
236
|
child = (
|
|
233
237
|
<StorageItemPreview
|
|
238
|
+
collection={collection}
|
|
234
239
|
name={`storage_preview_${entry.storagePathOrDownloadUrl}`}
|
|
235
240
|
property={property}
|
|
236
241
|
disabled={disabled}
|
|
@@ -294,6 +299,7 @@ function FileDropComponent({
|
|
|
294
299
|
|
|
295
300
|
export interface StorageUploadProps {
|
|
296
301
|
value: StorageFieldItem[];
|
|
302
|
+
collection: EntityCollection;
|
|
297
303
|
setInternalValue: (v: StorageFieldItem[]) => void;
|
|
298
304
|
name: string;
|
|
299
305
|
property: ResolvedStringProperty | ResolvedArrayProperty<string[]>;
|
|
@@ -309,6 +315,7 @@ export interface StorageUploadProps {
|
|
|
309
315
|
}
|
|
310
316
|
|
|
311
317
|
export function StorageUpload({
|
|
318
|
+
collection,
|
|
312
319
|
property,
|
|
313
320
|
name,
|
|
314
321
|
value,
|
|
@@ -321,7 +328,7 @@ export function StorageUpload({
|
|
|
321
328
|
autoFocus,
|
|
322
329
|
storage,
|
|
323
330
|
entity,
|
|
324
|
-
storagePathBuilder
|
|
331
|
+
storagePathBuilder,
|
|
325
332
|
}: StorageUploadProps) {
|
|
326
333
|
|
|
327
334
|
if (multipleFilesSupported) {
|
|
@@ -401,6 +408,7 @@ export function StorageUpload({
|
|
|
401
408
|
className="rounded"
|
|
402
409
|
>
|
|
403
410
|
<StorageItemPreview
|
|
411
|
+
collection={collection}
|
|
404
412
|
name={`storage_preview_${entry.storagePathOrDownloadUrl}`}
|
|
405
413
|
property={renderProperty}
|
|
406
414
|
disabled={true}
|
|
@@ -414,6 +422,7 @@ export function StorageUpload({
|
|
|
414
422
|
>
|
|
415
423
|
{(provided, snapshot) => {
|
|
416
424
|
return <FileDropComponent storage={storage}
|
|
425
|
+
collection={collection}
|
|
417
426
|
disabled={disabled}
|
|
418
427
|
isDraggingOver={snapshot.isDraggingOver}
|
|
419
428
|
droppableProvided={provided}
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
|
|
3
3
|
import { FieldProps } from "../../types";
|
|
4
|
-
import { useClearRestoreValue } from "../../hooks";
|
|
5
4
|
import { getIconForProperty } from "../../util";
|
|
6
|
-
import { FieldHelperText } from "../components
|
|
5
|
+
import { FieldHelperText, LabelWithIcon } from "../components";
|
|
7
6
|
import { BooleanSwitchWithLabel } from "@firecms/ui";
|
|
8
|
-
import {
|
|
7
|
+
import { useClearRestoreValue } from "../useClearRestoreValue";
|
|
9
8
|
|
|
10
9
|
type SwitchFieldProps = FieldProps<boolean>;
|
|
11
10
|
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import React, { useCallback } from "react";
|
|
2
2
|
|
|
3
|
+
import { ClearIcon, Collapse, IconButton, TextField } from "@firecms/ui";
|
|
3
4
|
import { FieldProps, PreviewType } from "../../types";
|
|
4
|
-
import { FieldHelperText } from "../components";
|
|
5
|
-
import { useClearRestoreValue } from "../../hooks";
|
|
5
|
+
import { FieldHelperText, LabelWithIcon } from "../components";
|
|
6
6
|
import { getIconForProperty } from "../../util";
|
|
7
|
-
import { ClearIcon, Collapse, IconButton, TextField } from "@firecms/ui";
|
|
8
7
|
import { PropertyPreview } from "../../preview";
|
|
9
|
-
import {
|
|
8
|
+
import { useClearRestoreValue } from "../useClearRestoreValue";
|
|
10
9
|
|
|
11
|
-
interface
|
|
10
|
+
interface TextFieldBindingProps<T extends string | number> extends FieldProps<T> {
|
|
12
11
|
allowInfinity?: boolean
|
|
13
12
|
}
|
|
14
13
|
|
|
@@ -19,6 +18,7 @@ interface TextFieldProps<T extends string | number> extends FieldProps<T> {
|
|
|
19
18
|
* @group Form fields
|
|
20
19
|
*/
|
|
21
20
|
export function TextFieldBinding<T extends string | number>({
|
|
21
|
+
context,
|
|
22
22
|
propertyKey,
|
|
23
23
|
value,
|
|
24
24
|
setValue,
|
|
@@ -28,7 +28,7 @@ export function TextFieldBinding<T extends string | number>({
|
|
|
28
28
|
autoFocus,
|
|
29
29
|
property,
|
|
30
30
|
includeDescription,
|
|
31
|
-
}:
|
|
31
|
+
}: TextFieldBindingProps<T>) {
|
|
32
32
|
|
|
33
33
|
let multiline: boolean | undefined;
|
|
34
34
|
let url: boolean | PreviewType | undefined;
|
|
@@ -97,9 +97,10 @@ export function TextFieldBinding<T extends string | number>({
|
|
|
97
97
|
{url && <Collapse
|
|
98
98
|
className="mt-1 ml-1"
|
|
99
99
|
in={Boolean(value)}>
|
|
100
|
-
<PropertyPreview
|
|
101
|
-
|
|
102
|
-
|
|
100
|
+
<PropertyPreview
|
|
101
|
+
value={value}
|
|
102
|
+
property={property}
|
|
103
|
+
size={"medium"}/>
|
|
103
104
|
</Collapse>}
|
|
104
105
|
|
|
105
106
|
</>
|
package/src/form/index.tsx
CHANGED
package/src/form/validation.ts
CHANGED
|
@@ -9,8 +9,7 @@ import {
|
|
|
9
9
|
} from "../types";
|
|
10
10
|
import * as yup from "yup";
|
|
11
11
|
import { AnySchema, ArraySchema, BooleanSchema, DateSchema, NumberSchema, ObjectSchema, StringSchema } from "yup";
|
|
12
|
-
import { enumToObjectEntries } from "../util
|
|
13
|
-
import { getValueInPath, hydrateRegExp, isPropertyBuilder } from "../util";
|
|
12
|
+
import { enumToObjectEntries, getValueInPath, hydrateRegExp, isPropertyBuilder } from "../util";
|
|
14
13
|
|
|
15
14
|
// Add custom unique function for array values
|
|
16
15
|
declare module "yup" {
|
|
@@ -90,12 +89,12 @@ export function mapPropertyToYup<T extends CMSType>(propertyContext: PropertyCon
|
|
|
90
89
|
throw Error("Unsupported data type in yup mapping");
|
|
91
90
|
}
|
|
92
91
|
|
|
93
|
-
export function getYupMapObjectSchema
|
|
92
|
+
export function getYupMapObjectSchema({
|
|
94
93
|
property,
|
|
95
94
|
entityId,
|
|
96
95
|
customFieldValidator,
|
|
97
96
|
name
|
|
98
|
-
}: PropertyContext<
|
|
97
|
+
}: PropertyContext<Record<string, any>>): ObjectSchema<any> {
|
|
99
98
|
const objectSchema: any = {};
|
|
100
99
|
const validation = property.validation;
|
|
101
100
|
if (property.properties)
|
|
@@ -70,7 +70,7 @@ export function useCollectionFetch<M extends Record<string, any>, UserType exten
|
|
|
70
70
|
searchString
|
|
71
71
|
}: CollectionFetchProps<M>): CollectionFetchResult<M> {
|
|
72
72
|
|
|
73
|
-
const dataSource = useDataSource();
|
|
73
|
+
const dataSource = useDataSource(collection);
|
|
74
74
|
const navigationController = useNavigationController();
|
|
75
75
|
|
|
76
76
|
const path = navigationController.resolveAliasesFrom(inputPath);
|
|
@@ -1,9 +1,14 @@
|
|
|
1
|
-
import { useContext } from "react";
|
|
2
|
-
import { DataSource } from "../../types";
|
|
1
|
+
import { useContext, useMemo } from "react";
|
|
2
|
+
import { DataSource, EntityCollection } from "../../types";
|
|
3
3
|
import { DataSourceContext } from "../../contexts/DataSourceContext";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Use this hook to get the datasource being used
|
|
7
7
|
* @group Hooks and utilities
|
|
8
8
|
*/
|
|
9
|
-
export const useDataSource = (): DataSource =>
|
|
9
|
+
export const useDataSource = (collection?: EntityCollection): DataSource => {
|
|
10
|
+
const defaultDataSource = useContext(DataSourceContext);
|
|
11
|
+
if (collection?.overrides?.dataSource)
|
|
12
|
+
return collection?.overrides.dataSource;
|
|
13
|
+
return defaultDataSource;
|
|
14
|
+
};
|
|
@@ -43,7 +43,7 @@ export function useEntityFetch<M extends Record<string, any>, UserType extends U
|
|
|
43
43
|
useCache = false
|
|
44
44
|
}: EntityFetchProps<M>): EntityFetchResult<M> {
|
|
45
45
|
|
|
46
|
-
const dataSource = useDataSource();
|
|
46
|
+
const dataSource = useDataSource(collection);
|
|
47
47
|
const navigationController = useNavigationController();
|
|
48
48
|
|
|
49
49
|
const path = navigationController.resolveAliasesFrom(inputPath);
|
package/src/hooks/index.tsx
CHANGED
|
@@ -4,7 +4,6 @@ export * from "./data/useEntityFetch";
|
|
|
4
4
|
export * from "./data/save";
|
|
5
5
|
export * from "./data/delete";
|
|
6
6
|
|
|
7
|
-
export * from "../form/useClearRestoreValue";
|
|
8
7
|
export * from "./useNavigationController";
|
|
9
8
|
|
|
10
9
|
export * from "./useResolvedNavigationFrom";
|
|
@@ -23,3 +22,6 @@ export * from "./useReferenceDialog";
|
|
|
23
22
|
export * from "./useBrowserTitleAndIcon";
|
|
24
23
|
export * from "./useCustomizationController";
|
|
25
24
|
export * from "./useBuildNavigationController";
|
|
25
|
+
|
|
26
|
+
export * from "./useBuildLocalConfigurationPersistence";
|
|
27
|
+
export * from "./useBuildModeController";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { useCallback, useEffect,
|
|
2
|
-
import
|
|
1
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
2
|
+
import equal from "react-fast-compare"
|
|
3
3
|
|
|
4
4
|
import {
|
|
5
5
|
AuthController,
|
|
@@ -10,12 +10,14 @@ import {
|
|
|
10
10
|
EntityCollectionsBuilder,
|
|
11
11
|
EntityReference,
|
|
12
12
|
NavigationController,
|
|
13
|
+
PermissionsBuilder,
|
|
13
14
|
TopNavigationEntry,
|
|
14
15
|
TopNavigationResult,
|
|
15
16
|
User,
|
|
16
17
|
UserConfigurationPersistence
|
|
17
18
|
} from "../types";
|
|
18
19
|
import {
|
|
20
|
+
applyPermissionsFunctionIfEmpty,
|
|
19
21
|
getCollectionByPathOrId,
|
|
20
22
|
mergeDeep,
|
|
21
23
|
removeInitialAndTrailingSlashes,
|
|
@@ -32,7 +34,10 @@ type BuildNavigationContextProps<EC extends EntityCollection, UserType extends U
|
|
|
32
34
|
baseCollectionPath?: string,
|
|
33
35
|
authController: AuthController<UserType>;
|
|
34
36
|
collections?: EC[] | EntityCollectionsBuilder<EC>;
|
|
37
|
+
collectionPermissions?: PermissionsBuilder;
|
|
35
38
|
views?: CMSView[] | CMSViewsBuilder;
|
|
39
|
+
adminViews?: CMSView[] | CMSViewsBuilder;
|
|
40
|
+
viewsOrder?: string[];
|
|
36
41
|
userConfigPersistence?: UserConfigurationPersistence;
|
|
37
42
|
dataSourceDelegate: DataSourceDelegate;
|
|
38
43
|
/**
|
|
@@ -45,21 +50,25 @@ type BuildNavigationContextProps<EC extends EntityCollection, UserType extends U
|
|
|
45
50
|
injectCollections?: (collections: EntityCollection[]) => EntityCollection[];
|
|
46
51
|
};
|
|
47
52
|
|
|
48
|
-
export function useBuildNavigationController<EC extends EntityCollection, UserType extends User>({
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
53
|
+
export function useBuildNavigationController<EC extends EntityCollection, UserType extends User>(props: BuildNavigationContextProps<EC, UserType>): NavigationController {
|
|
54
|
+
const {
|
|
55
|
+
basePath = DEFAULT_BASE_PATH,
|
|
56
|
+
baseCollectionPath = DEFAULT_COLLECTION_PATH,
|
|
57
|
+
authController,
|
|
58
|
+
collections: collectionsProp,
|
|
59
|
+
collectionPermissions,
|
|
60
|
+
views: viewsProp,
|
|
61
|
+
adminViews: adminViewsProp,
|
|
62
|
+
viewsOrder,
|
|
63
|
+
userConfigPersistence,
|
|
64
|
+
dataSourceDelegate,
|
|
65
|
+
injectCollections
|
|
66
|
+
} = props;
|
|
67
|
+
|
|
68
|
+
const collectionsRef = useRef<EntityCollection[] | null>();
|
|
61
69
|
const [collections, setCollections] = useState<EntityCollection[] | undefined>();
|
|
62
70
|
const [views, setViews] = useState<CMSView[] | undefined>();
|
|
71
|
+
const [adminViews, setAdminViews] = useState<CMSView[] | undefined>();
|
|
63
72
|
const [initialised, setInitialised] = useState<boolean>(false);
|
|
64
73
|
|
|
65
74
|
const [topLevelNavigation, setTopLevelNavigation] = useState<TopNavigationResult | undefined>(undefined);
|
|
@@ -79,19 +88,18 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
|
|
|
79
88
|
const buildUrlCollectionPath = useCallback((path: string): string => `${removeInitialAndTrailingSlashes(baseCollectionPath)}/${encodePath(path)}`,
|
|
80
89
|
[baseCollectionPath]);
|
|
81
90
|
|
|
82
|
-
const computeTopNavigation = useCallback((collections: EntityCollection[], views: CMSView[]): TopNavigationResult => {
|
|
83
|
-
|
|
84
|
-
const navigationEntries: TopNavigationEntry[] = [
|
|
91
|
+
const computeTopNavigation = useCallback((collections: EntityCollection[], views: CMSView[], adminViews: CMSView[], viewsOrder?: string[]): TopNavigationResult => {
|
|
92
|
+
let navigationEntries: TopNavigationEntry[] = [
|
|
85
93
|
...(collections ?? []).map(collection => (!collection.hideFromNavigation
|
|
86
|
-
? {
|
|
94
|
+
? ({
|
|
87
95
|
url: buildUrlCollectionPath(collection.id ?? collection.path),
|
|
88
96
|
type: "collection",
|
|
89
97
|
name: collection.name.trim(),
|
|
90
98
|
path: collection.id ?? collection.path,
|
|
91
99
|
collection,
|
|
92
100
|
description: collection.description?.trim(),
|
|
93
|
-
group: collection.group?.trim()
|
|
94
|
-
}
|
|
101
|
+
group: collection.group?.trim() ?? "Views"
|
|
102
|
+
} satisfies TopNavigationEntry)
|
|
95
103
|
: undefined))
|
|
96
104
|
.filter(Boolean) as TopNavigationEntry[],
|
|
97
105
|
...(views ?? []).map(view =>
|
|
@@ -100,18 +108,50 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
|
|
|
100
108
|
url: buildCMSUrlPath(Array.isArray(view.path) ? view.path[0] : view.path),
|
|
101
109
|
name: view.name.trim(),
|
|
102
110
|
type: "view",
|
|
111
|
+
path: view.path,
|
|
103
112
|
view,
|
|
104
113
|
description: view.description?.trim(),
|
|
105
|
-
group: view.group?.trim()
|
|
106
|
-
})
|
|
114
|
+
group: view.group?.trim() ?? "Views"
|
|
115
|
+
}satisfies TopNavigationEntry)
|
|
116
|
+
: undefined)
|
|
117
|
+
.filter(Boolean) as TopNavigationEntry[],
|
|
118
|
+
...(adminViews ?? []).map(view =>
|
|
119
|
+
!view.hideFromNavigation
|
|
120
|
+
? ({
|
|
121
|
+
url: buildCMSUrlPath(Array.isArray(view.path) ? view.path[0] : view.path),
|
|
122
|
+
name: view.name.trim(),
|
|
123
|
+
type: "admin",
|
|
124
|
+
path: view.path,
|
|
125
|
+
view,
|
|
126
|
+
description: view.description?.trim(),
|
|
127
|
+
group: "Admin"
|
|
128
|
+
} satisfies TopNavigationEntry)
|
|
107
129
|
: undefined)
|
|
108
130
|
.filter(Boolean) as TopNavigationEntry[]
|
|
109
131
|
];
|
|
110
132
|
|
|
133
|
+
if (viewsOrder) {
|
|
134
|
+
navigationEntries = navigationEntries.sort((a, b) => {
|
|
135
|
+
const aIndex = viewsOrder.indexOf(a.path);
|
|
136
|
+
const bIndex = viewsOrder.indexOf(b.path);
|
|
137
|
+
if (aIndex === -1 && bIndex === -1) {
|
|
138
|
+
return 0;
|
|
139
|
+
}
|
|
140
|
+
if (aIndex === -1) {
|
|
141
|
+
return 1;
|
|
142
|
+
}
|
|
143
|
+
if (bIndex === -1) {
|
|
144
|
+
return -1;
|
|
145
|
+
}
|
|
146
|
+
return aIndex - bIndex;
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
|
|
111
150
|
const groups: string[] = Object.values(navigationEntries)
|
|
112
151
|
.map(e => e.group)
|
|
113
152
|
.filter(Boolean)
|
|
114
153
|
.filter((value, index, array) => array.indexOf(value) === index) as string[];
|
|
154
|
+
|
|
115
155
|
return {
|
|
116
156
|
navigationEntries,
|
|
117
157
|
groups
|
|
@@ -124,15 +164,24 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
|
|
|
124
164
|
return;
|
|
125
165
|
|
|
126
166
|
try {
|
|
127
|
-
const [resolvedCollections = [], resolvedViews = []] = await Promise.all([
|
|
128
|
-
resolveCollections(
|
|
129
|
-
resolveCMSViews(
|
|
167
|
+
const [resolvedCollections = [], resolvedViews, resolvedAdminViews = []] = await Promise.all([
|
|
168
|
+
resolveCollections(collectionsProp, collectionPermissions, authController, dataSourceDelegate, injectCollections),
|
|
169
|
+
resolveCMSViews(viewsProp, authController, dataSourceDelegate),
|
|
170
|
+
resolveCMSViews(adminViewsProp, authController, dataSourceDelegate)
|
|
130
171
|
]
|
|
131
172
|
);
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
173
|
+
if (
|
|
174
|
+
!equal(collectionsRef.current, resolvedCollections) ||
|
|
175
|
+
!equal(views, resolvedViews) ||
|
|
176
|
+
!equal(adminViews, resolvedAdminViews) ||
|
|
177
|
+
!equal(topLevelNavigation, computeTopNavigation(resolvedCollections, resolvedViews, resolvedAdminViews, viewsOrder))
|
|
178
|
+
) {
|
|
179
|
+
collectionsRef.current = resolvedCollections;
|
|
180
|
+
setCollections(resolvedCollections);
|
|
181
|
+
setViews(resolvedViews);
|
|
182
|
+
setAdminViews(resolvedAdminViews);
|
|
183
|
+
setTopLevelNavigation(computeTopNavigation(resolvedCollections ?? [], resolvedViews, resolvedAdminViews, viewsOrder));
|
|
184
|
+
}
|
|
136
185
|
} catch (e) {
|
|
137
186
|
console.error(e);
|
|
138
187
|
setNavigationLoadingError(e as any);
|
|
@@ -140,7 +189,7 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
|
|
|
140
189
|
|
|
141
190
|
setNavigationLoading(false);
|
|
142
191
|
setInitialised(true);
|
|
143
|
-
}, [
|
|
192
|
+
}, [collectionsProp, collectionPermissions, authController.user, authController.initialLoading, viewsProp, adminViewsProp, computeTopNavigation, injectCollections]);
|
|
144
193
|
|
|
145
194
|
useEffect(() => {
|
|
146
195
|
refreshNavigation();
|
|
@@ -182,7 +231,7 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
|
|
|
182
231
|
}, [
|
|
183
232
|
basePath,
|
|
184
233
|
baseCollectionPath,
|
|
185
|
-
collections
|
|
234
|
+
collections
|
|
186
235
|
]);
|
|
187
236
|
|
|
188
237
|
const getCollectionFromPaths = useCallback(<EC extends EntityCollection>(pathSegments: string[]): EC | undefined => {
|
|
@@ -248,15 +297,7 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
|
|
|
248
297
|
return resolveCollectionPathIds(path, collections);
|
|
249
298
|
}, [collections]);
|
|
250
299
|
|
|
251
|
-
const
|
|
252
|
-
/**
|
|
253
|
-
* The location can be overridden if `base_location` is set in the
|
|
254
|
-
* state field of the current location. This can happen if you open
|
|
255
|
-
* a side entity, like `products`, from a different one, like `users`
|
|
256
|
-
*/
|
|
257
|
-
const baseLocation = state && state.base_location ? state.base_location : location;
|
|
258
|
-
|
|
259
|
-
const getAllParentCollectionsForPath = useCallback((path: string): EntityReference[] => {
|
|
300
|
+
const getAllParentReferencesForPath = useCallback((path: string): EntityReference[] => {
|
|
260
301
|
return getParentReferencesFromPath({
|
|
261
302
|
path,
|
|
262
303
|
collections
|
|
@@ -264,8 +305,20 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
|
|
|
264
305
|
}, [collections]);
|
|
265
306
|
|
|
266
307
|
const getParentCollectionIds = useCallback((path: string): string[] => {
|
|
267
|
-
|
|
268
|
-
|
|
308
|
+
|
|
309
|
+
const strings = path.split("/");
|
|
310
|
+
const oddPathSegments = strings.filter((_, i) => i % 2 === 0);
|
|
311
|
+
oddPathSegments.pop();
|
|
312
|
+
|
|
313
|
+
const result: string[][] = [];
|
|
314
|
+
|
|
315
|
+
for (let i = 1; i <= oddPathSegments.length; i++) {
|
|
316
|
+
result.push(oddPathSegments.slice(0, i));
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// for each odd path segment, get the collection
|
|
320
|
+
return result.map(r => getCollectionFromPaths(r)?.id).filter(Boolean) as string[];
|
|
321
|
+
}, [getAllParentReferencesForPath])
|
|
269
322
|
|
|
270
323
|
const convertIdsToPaths = useCallback((ids: string[]): string[] => {
|
|
271
324
|
let currentCollections = collections;
|
|
@@ -282,9 +335,10 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
|
|
|
282
335
|
}
|
|
283
336
|
, [getCollectionFromIds]);
|
|
284
337
|
|
|
285
|
-
return
|
|
338
|
+
return {
|
|
286
339
|
collections,
|
|
287
340
|
views,
|
|
341
|
+
adminViews,
|
|
288
342
|
loading: !initialised || navigationLoading,
|
|
289
343
|
navigationLoadingError,
|
|
290
344
|
homeUrl,
|
|
@@ -301,12 +355,11 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
|
|
|
301
355
|
buildCMSUrlPath,
|
|
302
356
|
resolveAliasesFrom,
|
|
303
357
|
topLevelNavigation,
|
|
304
|
-
baseLocation,
|
|
305
358
|
refreshNavigation,
|
|
306
|
-
getParentReferencesFromPath:
|
|
359
|
+
getParentReferencesFromPath: getAllParentReferencesForPath,
|
|
307
360
|
getParentCollectionIds,
|
|
308
361
|
convertIdsToPaths
|
|
309
|
-
}
|
|
362
|
+
};
|
|
310
363
|
}
|
|
311
364
|
|
|
312
365
|
export function getSidePanelKey(path: string, entityId?: string) {
|
|
@@ -326,8 +379,8 @@ function filterOutNotAllowedCollections(resolvedCollections: EntityCollection[],
|
|
|
326
379
|
return resolvedCollections
|
|
327
380
|
.filter((c) => {
|
|
328
381
|
if (!c.permissions) return true;
|
|
329
|
-
const resolvedPermissions = resolvePermissions(c, authController,
|
|
330
|
-
return resolvedPermissions
|
|
382
|
+
const resolvedPermissions = resolvePermissions(c, authController, c.path, null)
|
|
383
|
+
return resolvedPermissions?.read !== false;
|
|
331
384
|
})
|
|
332
385
|
.map((c) => {
|
|
333
386
|
if (!c.subcollections) return c;
|
|
@@ -339,6 +392,7 @@ function filterOutNotAllowedCollections(resolvedCollections: EntityCollection[],
|
|
|
339
392
|
}
|
|
340
393
|
|
|
341
394
|
async function resolveCollections(collections: undefined | EntityCollection[] | EntityCollectionsBuilder<any>,
|
|
395
|
+
collectionPermissions: PermissionsBuilder | undefined,
|
|
342
396
|
authController: AuthController,
|
|
343
397
|
dataSource: DataSourceDelegate,
|
|
344
398
|
injectCollections?: (collections: EntityCollection[]) => EntityCollection[]) {
|
|
@@ -353,6 +407,8 @@ async function resolveCollections(collections: undefined | EntityCollection[] |
|
|
|
353
407
|
resolvedCollections = collections;
|
|
354
408
|
}
|
|
355
409
|
|
|
410
|
+
resolvedCollections = applyPermissionsFunctionIfEmpty(resolvedCollections, collectionPermissions);
|
|
411
|
+
|
|
356
412
|
resolvedCollections = filterOutNotAllowedCollections(resolvedCollections, authController);
|
|
357
413
|
|
|
358
414
|
if (injectCollections) {
|
|
@@ -1,9 +1,14 @@
|
|
|
1
|
-
import { useEffect, useRef } from "react";
|
|
1
|
+
import { useEffect, useRef, useState } from "react";
|
|
2
2
|
import { AuthController } from "../types";
|
|
3
3
|
|
|
4
4
|
export const DEFAULT_SERVER_DEV = "https://api-kdoe6pj3qq-ey.a.run.app";
|
|
5
5
|
export const DEFAULT_SERVER = "https://api-drplyi3b6q-ey.a.run.app";
|
|
6
6
|
|
|
7
|
+
export type AccessResponse = {
|
|
8
|
+
blocked?: boolean;
|
|
9
|
+
message?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
7
12
|
async function makeRequest(authController: AuthController) {
|
|
8
13
|
const firebaseToken = await authController.getAuthToken();
|
|
9
14
|
return fetch(DEFAULT_SERVER + "/access_log",
|
|
@@ -17,15 +22,18 @@ async function makeRequest(authController: AuthController) {
|
|
|
17
22
|
body: JSON.stringify({})
|
|
18
23
|
})
|
|
19
24
|
.then(async (res) => {
|
|
25
|
+
return res.json();
|
|
20
26
|
});
|
|
21
27
|
}
|
|
22
28
|
|
|
23
|
-
export function useProjectLog(authController: AuthController) {
|
|
29
|
+
export function useProjectLog(authController: AuthController): AccessResponse | null {
|
|
30
|
+
const [accessResponse, setAccessResponse] = useState<AccessResponse | null>(null);
|
|
24
31
|
const accessedUserRef = useRef<string | null>(null);
|
|
25
32
|
useEffect(() => {
|
|
26
33
|
if (authController.user && authController.user.uid !== accessedUserRef.current && !authController.initialLoading) {
|
|
27
|
-
makeRequest(authController);
|
|
34
|
+
makeRequest(authController).then(setAccessResponse);
|
|
28
35
|
accessedUserRef.current = authController.user.uid;
|
|
29
36
|
}
|
|
30
37
|
}, [authController]);
|
|
38
|
+
return accessResponse;
|
|
31
39
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useSideDialogsController } from "./useSideDialogsController";
|
|
2
|
-
import {
|
|
2
|
+
import { ReferenceSelectionInnerProps, ReferenceSelectionTable } from "../components";
|
|
3
3
|
import { useCallback } from "react";
|
|
4
4
|
import { useNavigationController } from "./useNavigationController";
|
|
5
5
|
|
|
@@ -27,7 +27,7 @@ export function useReferenceDialog<M extends Record<string, any>>(referenceDialo
|
|
|
27
27
|
if (!usedCollection)
|
|
28
28
|
usedCollection = navigation.getCollection(referenceDialogProps.path);
|
|
29
29
|
if (!usedCollection)
|
|
30
|
-
throw Error("Not able to resolve the collection in useReferenceDialog");
|
|
30
|
+
throw Error("Not able to resolve the collection in useReferenceDialog. Make sure a collection is registered in path " + referenceDialogProps.path);
|
|
31
31
|
sideDialogsController.open({
|
|
32
32
|
key: `reference_${referenceDialogProps.path}`,
|
|
33
33
|
component:
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { StorageSource } from "../types";
|
|
1
|
+
import { EntityCollection, StorageSource } from "../types";
|
|
2
2
|
import { StorageSourceContext } from "../contexts/StorageSourceContext";
|
|
3
3
|
import { useContext } from "react";
|
|
4
4
|
|
|
@@ -6,4 +6,9 @@ import { useContext } from "react";
|
|
|
6
6
|
* Use this hook to get the storage source being used
|
|
7
7
|
* @group Hooks and utilities
|
|
8
8
|
*/
|
|
9
|
-
export const useStorageSource = (): StorageSource =>
|
|
9
|
+
export const useStorageSource = (collection?: EntityCollection): StorageSource => {
|
|
10
|
+
const defaultStorageSource = useContext(StorageSourceContext);
|
|
11
|
+
if (collection?.overrides?.storageSource)
|
|
12
|
+
return collection.overrides.storageSource;
|
|
13
|
+
return defaultStorageSource;
|
|
14
|
+
};
|