@firecms/core 3.1.0-canary.9e89e98 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/EntityCollectionTable/internal/popup_field/useDraggable.d.ts +2 -2
- package/dist/components/ErrorBoundary.d.ts +1 -1
- package/dist/components/VirtualTable/VirtualTableHeader.d.ts +1 -1
- package/dist/form/components/ErrorFocus.d.ts +1 -1
- package/dist/index.es.js +118 -54
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +118 -54
- package/dist/index.umd.js.map +1 -1
- package/dist/internal/useRestoreScroll.d.ts +1 -1
- package/dist/types/analytics.d.ts +1 -1
- package/dist/types/plugins.d.ts +16 -0
- package/dist/util/entities.d.ts +1 -1
- package/dist/util/resolutions.d.ts +2 -2
- package/package.json +9 -9
- package/src/components/EntityCollectionTable/internal/EntityTableCellActions.tsx +1 -1
- package/src/components/EntityCollectionTable/internal/popup_field/useDraggable.tsx +11 -11
- package/src/components/EntityCollectionView/EntityBoardCard.tsx +1 -1
- package/src/components/EntityCollectionView/EntityCard.tsx +4 -0
- package/src/components/EntityCollectionView/EntityCollectionBoardView.tsx +23 -3
- package/src/components/EntityCollectionView/EntityCollectionView.tsx +32 -3
- package/src/components/VirtualTable/VirtualTable.tsx +116 -113
- package/src/components/VirtualTable/VirtualTableHeader.tsx +42 -42
- package/src/components/VirtualTable/VirtualTableHeaderRow.tsx +1 -1
- package/src/components/VirtualTable/fields/VirtualTableSelect.tsx +3 -3
- package/src/core/DefaultAppBar.tsx +1 -1
- package/src/core/EntitySidePanel.tsx +28 -26
- package/src/core/field_configs.tsx +14 -9
- package/src/form/EntityForm.tsx +69 -60
- package/src/form/PropertyFieldBinding.tsx +3 -3
- package/src/form/components/ErrorFocus.tsx +3 -3
- package/src/form/field_bindings/MarkdownEditorFieldBinding.tsx +1 -1
- package/src/form/field_bindings/StorageUploadFieldBinding.tsx +83 -83
- package/src/hooks/useBuildNavigationController.tsx +4 -4
- package/src/hooks/useValidateAuthenticator.tsx +1 -1
- package/src/internal/useBuildDataSource.ts +1 -2
- package/src/preview/PropertyPreview.tsx +1 -0
- package/src/types/analytics.ts +10 -0
- package/src/types/plugins.tsx +18 -0
- package/src/util/entities.ts +1 -1
- package/src/util/join_collections.ts +10 -8
- package/src/util/previews.ts +2 -2
- package/src/util/property_utils.tsx +1 -1
- package/src/util/resolutions.ts +5 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
export type CMSAnalyticsEvent = "entity_click" | "entity_click_from_reference" | "reference_selection_clear" | "reference_selection_toggle" | "reference_selected_single" | "reference_selection_new_entity" | "edit_entity_clicked" | "entity_edited" | "new_entity_click" | "new_entity_saved" | "copy_entity_click" | "entity_copied" | "single_delete_dialog_open" | "multiple_delete_dialog_open" | "single_entity_deleted" | "multiple_entities_deleted" | "drawer_navigate_to_home" | "drawer_navigate_to_collection" | "drawer_navigate_to_view" | "home_navigate_to_collection" | "home_favorite_navigate_to_collection" | "home_navigate_to_view" | "home_navigate_to_admin_view" | "home_favorite_navigate_to_view" | "home_move_card" | "home_move_group" | "home_drop_new_group" | "collection_inline_editing" | "unmapped_event";
|
|
1
|
+
export type CMSAnalyticsEvent = "entity_click" | "entity_click_from_reference" | "reference_selection_clear" | "reference_selection_toggle" | "reference_selected_single" | "reference_selection_new_entity" | "edit_entity_clicked" | "entity_edited" | "new_entity_click" | "new_entity_saved" | "copy_entity_click" | "entity_copied" | "single_delete_dialog_open" | "multiple_delete_dialog_open" | "single_entity_deleted" | "multiple_entities_deleted" | "drawer_navigate_to_home" | "drawer_navigate_to_collection" | "drawer_navigate_to_view" | "home_navigate_to_collection" | "home_favorite_navigate_to_collection" | "home_navigate_to_view" | "home_navigate_to_admin_view" | "home_favorite_navigate_to_view" | "home_move_card" | "home_move_group" | "home_drop_new_group" | "collection_inline_editing" | "view_mode_changed" | "kanban_card_moved" | "kanban_column_reorder" | "kanban_property_changed" | "kanban_new_entity_in_column" | "kanban_backfill_order" | "card_view_entity_click" | "unmapped_event";
|
package/dist/types/plugins.d.ts
CHANGED
|
@@ -91,6 +91,18 @@ export type FireCMSPlugin<PROPS = any, FORM_PROPS = any, EC extends EntityCollec
|
|
|
91
91
|
onNavigationEntriesUpdate?: (entries: NavigationGroupMapping[]) => void;
|
|
92
92
|
};
|
|
93
93
|
collectionView?: {
|
|
94
|
+
/**
|
|
95
|
+
* Custom component to render when a collection loading error occurs.
|
|
96
|
+
* If provided, this replaces the default error view in all collection view modes
|
|
97
|
+
* (table, card, kanban).
|
|
98
|
+
* Return `null` from the component to fall back to the default error view.
|
|
99
|
+
*/
|
|
100
|
+
CollectionError?: React.ComponentType<{
|
|
101
|
+
path: string;
|
|
102
|
+
collection: EC;
|
|
103
|
+
parentCollectionIds?: string[];
|
|
104
|
+
error: Error;
|
|
105
|
+
}>;
|
|
94
106
|
/**
|
|
95
107
|
* Use this component to add custom actions to the entity collections
|
|
96
108
|
* toolbar.
|
|
@@ -194,6 +206,10 @@ export type FireCMSPlugin<PROPS = any, FORM_PROPS = any, EC extends EntityCollec
|
|
|
194
206
|
* Add custom actions to the top of the form
|
|
195
207
|
*/
|
|
196
208
|
ActionsTop?: React.ComponentType<PluginFormActionProps<any, EC>>;
|
|
209
|
+
/**
|
|
210
|
+
* Add custom content above the entity title in the form view
|
|
211
|
+
*/
|
|
212
|
+
BeforeTitle?: React.ComponentType<PluginFormActionProps<any, EC>>;
|
|
197
213
|
fieldBuilder?: <T extends CMSType = CMSType>(props: PluginFieldBuilderParams<T, any, EC>) => React.ComponentType<FieldProps<T>> | null;
|
|
198
214
|
fieldBuilderEnabled?: <T extends CMSType = CMSType>(props: PluginFieldBuilderParams<T>) => boolean;
|
|
199
215
|
};
|
package/dist/util/entities.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { CMSType, DataType, Entity, EntityReference, EntityStatus, EntityValues, PropertiesOrBuilders, Property, PropertyBuilder, PropertyOrBuilder, ResolvedProperties, ResolvedProperty } from "../types";
|
|
2
2
|
export declare function isReadOnly(property: Property<any> | ResolvedProperty<any>): boolean;
|
|
3
3
|
export declare function isHidden(property: Property | ResolvedProperty): boolean;
|
|
4
|
-
export declare function isPropertyBuilder<T extends CMSType, M extends Record<string, any
|
|
4
|
+
export declare function isPropertyBuilder<T extends CMSType = CMSType, M extends Record<string, any> = any>(propertyOrBuilder?: PropertyOrBuilder<T, M> | Property | ResolvedProperty): propertyOrBuilder is PropertyBuilder<T, M>;
|
|
5
5
|
export declare function getDefaultValuesFor<M extends Record<string, any>>(properties: PropertiesOrBuilders<M> | ResolvedProperties<M>): Partial<EntityValues<M>>;
|
|
6
6
|
export declare function getDefaultValueFor(property?: PropertyOrBuilder): {} | null | undefined;
|
|
7
7
|
export declare function getDefaultValueForDataType(dataType: DataType): {} | null;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ArrayProperty, AuthController, CMSType, CustomizationController, EntityAction, EntityCollection, EntityCustomView, EntityValues, EnumValueConfig, EnumValues, NumberProperty, PropertiesOrBuilders, PropertyConfig, PropertyOrBuilder, ResolvedArrayProperty, ResolvedEntityCollection, ResolvedNumberProperty, ResolvedProperties, ResolvedProperty, ResolvedStringProperty, StringProperty, UserConfigurationPersistence } from "../types";
|
|
1
|
+
import { ArrayProperty, AuthController, CMSType, CustomizationController, EntityAction, EntityCollection, EntityCustomView, EntityValues, EnumValueConfig, EnumValues, NumberProperty, PropertiesOrBuilders, Property, PropertyConfig, PropertyOrBuilder, ResolvedArrayProperty, ResolvedEntityCollection, ResolvedNumberProperty, ResolvedProperties, ResolvedProperty, ResolvedStringProperty, StringProperty, UserConfigurationPersistence } from "../types";
|
|
2
2
|
export declare const resolveCollection: <M extends Record<string, any>>({ collection, path, entityId, values, previousValues, userConfigPersistence, propertyConfigs, ignoreMissingFields, authController }: {
|
|
3
3
|
collection: EntityCollection<M> | ResolvedEntityCollection<M>;
|
|
4
4
|
path: string;
|
|
@@ -17,7 +17,7 @@ export declare const resolveCollection: <M extends Record<string, any>>({ collec
|
|
|
17
17
|
*/
|
|
18
18
|
export declare function resolveProperty<T extends CMSType = CMSType, M extends Record<string, any> = any>({ propertyOrBuilder, fromBuilder, ignoreMissingFields, ...props }: {
|
|
19
19
|
propertyKey?: string;
|
|
20
|
-
propertyOrBuilder: PropertyOrBuilder<T, M> | ResolvedProperty<T
|
|
20
|
+
propertyOrBuilder: PropertyOrBuilder<T, M> | ResolvedProperty<T> | PropertyOrBuilder | Property | ResolvedProperty | undefined;
|
|
21
21
|
values?: Partial<M>;
|
|
22
22
|
previousValues?: Partial<M>;
|
|
23
23
|
path?: string;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@firecms/core",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "3.1.0
|
|
4
|
+
"version": "3.1.0",
|
|
5
5
|
"description": "Awesome Firebase/Firestore-based headless open-source CMS",
|
|
6
6
|
"funding": {
|
|
7
7
|
"url": "https://github.com/sponsors/firecmsco"
|
|
@@ -53,9 +53,9 @@
|
|
|
53
53
|
"@dnd-kit/core": "^6.3.1",
|
|
54
54
|
"@dnd-kit/modifiers": "^9.0.0",
|
|
55
55
|
"@dnd-kit/sortable": "^10.0.0",
|
|
56
|
-
"@firecms/editor": "^3.1.0
|
|
57
|
-
"@firecms/formex": "^3.1.0
|
|
58
|
-
"@firecms/ui": "^3.1.0
|
|
56
|
+
"@firecms/editor": "^3.1.0",
|
|
57
|
+
"@firecms/formex": "^3.1.0",
|
|
58
|
+
"@firecms/ui": "^3.1.0",
|
|
59
59
|
"@radix-ui/react-portal": "^1.1.10",
|
|
60
60
|
"clsx": "^2.1.1",
|
|
61
61
|
"compressorjs": "^1.2.1",
|
|
@@ -76,8 +76,8 @@
|
|
|
76
76
|
"yup": "^1.7.1"
|
|
77
77
|
},
|
|
78
78
|
"peerDependencies": {
|
|
79
|
-
"react": ">=18.0.0",
|
|
80
|
-
"react-dom": ">=18.0.0",
|
|
79
|
+
"react": ">=18.3.1 || >=19.0.0",
|
|
80
|
+
"react-dom": ">=18.3.1 || >=19.0.0",
|
|
81
81
|
"react-router": "^6.28.0",
|
|
82
82
|
"react-router-dom": "^6.28.0"
|
|
83
83
|
},
|
|
@@ -89,8 +89,8 @@
|
|
|
89
89
|
"@types/json-logic-js": "^2.0.8",
|
|
90
90
|
"@types/node": "^20.19.17",
|
|
91
91
|
"@types/object-hash": "^3.0.6",
|
|
92
|
-
"@types/react": "^
|
|
93
|
-
"@types/react-dom": "^
|
|
92
|
+
"@types/react": "^19.2.3",
|
|
93
|
+
"@types/react-dom": "^19.2.3",
|
|
94
94
|
"@types/react-measure": "^2.0.12",
|
|
95
95
|
"@vitejs/plugin-react": "^4.7.0",
|
|
96
96
|
"babel-plugin-react-compiler": "^19.0.0-beta-af1b7da-20250417",
|
|
@@ -111,7 +111,7 @@
|
|
|
111
111
|
"dist",
|
|
112
112
|
"src"
|
|
113
113
|
],
|
|
114
|
-
"gitHead": "
|
|
114
|
+
"gitHead": "40f8d9860cb2649c0a195ecebd1a92ccb37f33a6",
|
|
115
115
|
"publishConfig": {
|
|
116
116
|
"access": "public"
|
|
117
117
|
},
|
|
@@ -28,7 +28,7 @@ export function EntityTableCellActions({
|
|
|
28
28
|
}
|
|
29
29
|
}, []);
|
|
30
30
|
|
|
31
|
-
const iconRef = useRef<HTMLButtonElement>();
|
|
31
|
+
const iconRef = useRef<HTMLButtonElement>(undefined);
|
|
32
32
|
useEffect(() => {
|
|
33
33
|
if (iconRef.current && selected) {
|
|
34
34
|
iconRef.current.focus({ preventScroll: true });
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import React, { useCallback, useEffect } from "react";
|
|
2
2
|
|
|
3
3
|
interface DraggableProps {
|
|
4
|
-
containerRef: React.RefObject<HTMLDivElement>,
|
|
5
|
-
innerRef: React.RefObject<HTMLDivElement>,
|
|
4
|
+
containerRef: React.RefObject<HTMLDivElement | null>,
|
|
5
|
+
innerRef: React.RefObject<HTMLDivElement | null>,
|
|
6
6
|
x?: number;
|
|
7
7
|
y?: number;
|
|
8
8
|
onMove: (params: { x: number, y: number }) => void,
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
export function useDraggable({
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
12
|
+
containerRef,
|
|
13
|
+
innerRef,
|
|
14
|
+
x,
|
|
15
|
+
y,
|
|
16
|
+
onMove
|
|
17
|
+
}: DraggableProps) {
|
|
18
18
|
|
|
19
19
|
let relX = 0;
|
|
20
20
|
let relY = 0;
|
|
@@ -62,9 +62,9 @@ export function useDraggable({
|
|
|
62
62
|
if (event.target.localName === "input" || !listeningRef.current)
|
|
63
63
|
return;
|
|
64
64
|
onMove({
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
65
|
+
x: event.screenX - relX,
|
|
66
|
+
y: event.screenY - relY
|
|
67
|
+
}
|
|
68
68
|
);
|
|
69
69
|
event.stopPropagation();
|
|
70
70
|
};
|
|
@@ -149,7 +149,7 @@ function EntityBoardCardInner<M extends Record<string, any> = any>({
|
|
|
149
149
|
{/* Content */}
|
|
150
150
|
<div className="flex-1 min-w-0">
|
|
151
151
|
{/* Title */}
|
|
152
|
-
<div className="
|
|
152
|
+
<div className="line-clamp-2 text-sm font-medium">
|
|
153
153
|
{titleProperty && titleValue ? (
|
|
154
154
|
<PropertyPreview
|
|
155
155
|
propertyKey={titlePropertyKey as string}
|
|
@@ -111,6 +111,10 @@ export function EntityCard<M extends Record<string, any> = any>({
|
|
|
111
111
|
return;
|
|
112
112
|
}
|
|
113
113
|
if (onClick) {
|
|
114
|
+
analyticsController.onAnalyticsEvent?.("card_view_entity_click", {
|
|
115
|
+
path: entity.path,
|
|
116
|
+
entityId: entity.id
|
|
117
|
+
});
|
|
114
118
|
onClick(entity);
|
|
115
119
|
}
|
|
116
120
|
};
|
|
@@ -34,6 +34,7 @@ import {
|
|
|
34
34
|
useFireCMSContext,
|
|
35
35
|
useSideEntityController
|
|
36
36
|
} from "../../hooks";
|
|
37
|
+
import { useAnalyticsController } from "../../hooks/useAnalyticsController";
|
|
37
38
|
import { SaveEntityProps } from "../../types/datasource";
|
|
38
39
|
import { setIn } from "@firecms/formex";
|
|
39
40
|
import { useBoardDataController } from "./useBoardDataController";
|
|
@@ -74,6 +75,7 @@ export function EntityCollectionBoardView<M extends Record<string, any> = any>({
|
|
|
74
75
|
const context = useFireCMSContext();
|
|
75
76
|
const dataSource = useDataSource(collection);
|
|
76
77
|
const sideEntityController = useSideEntityController();
|
|
78
|
+
const analyticsController = useAnalyticsController();
|
|
77
79
|
const plugins = customizationController.plugins ?? [];
|
|
78
80
|
|
|
79
81
|
// State for backfill dialog
|
|
@@ -222,6 +224,10 @@ export function EntityCollectionBoardView<M extends Record<string, any> = any>({
|
|
|
222
224
|
}, [plugins]);
|
|
223
225
|
|
|
224
226
|
const handleColumnReorder = useCallback((newColumns: string[]) => {
|
|
227
|
+
analyticsController.onAnalyticsEvent?.("kanban_column_reorder", {
|
|
228
|
+
path: fullPath,
|
|
229
|
+
columnProperty
|
|
230
|
+
});
|
|
225
231
|
setHasUserReordered(true);
|
|
226
232
|
setLocalColumnsOrder(newColumns);
|
|
227
233
|
plugins
|
|
@@ -235,7 +241,7 @@ export function EntityCollectionBoardView<M extends Record<string, any> = any>({
|
|
|
235
241
|
newColumnsOrder: newColumns
|
|
236
242
|
});
|
|
237
243
|
});
|
|
238
|
-
}, [plugins, fullPath, parentCollectionIds, collection, columnProperty]);
|
|
244
|
+
}, [plugins, fullPath, parentCollectionIds, collection, columnProperty, analyticsController]);
|
|
239
245
|
|
|
240
246
|
// Collection-level count queries to detect missing order property
|
|
241
247
|
// Just TWO counts: total and ordered (for the entire collection, not per column)
|
|
@@ -393,6 +399,13 @@ export function EntityCollectionBoardView<M extends Record<string, any> = any>({
|
|
|
393
399
|
const entity = items.find(item => item.id === moveInfo?.itemId)?.entity;
|
|
394
400
|
if (!entity) return;
|
|
395
401
|
|
|
402
|
+
analyticsController.onAnalyticsEvent?.("kanban_card_moved", {
|
|
403
|
+
path: fullPath,
|
|
404
|
+
entityId: entity.id,
|
|
405
|
+
sourceColumn: moveInfo?.sourceColumn,
|
|
406
|
+
targetColumn: moveInfo?.targetColumn
|
|
407
|
+
});
|
|
408
|
+
|
|
396
409
|
const isColumnChange = moveInfo && moveInfo.sourceColumn !== moveInfo.targetColumn;
|
|
397
410
|
|
|
398
411
|
// If no orderProperty and not a column change, nothing to do
|
|
@@ -440,7 +453,7 @@ export function EntityCollectionBoardView<M extends Record<string, any> = any>({
|
|
|
440
453
|
} catch (e) {
|
|
441
454
|
console.error("Error saving entity:", e);
|
|
442
455
|
}
|
|
443
|
-
}, [collection, columnProperty, orderProperty, context, dataSource, calculateNewOrder, boardDataController]);
|
|
456
|
+
}, [collection, columnProperty, orderProperty, context, dataSource, calculateNewOrder, boardDataController, analyticsController, fullPath]);
|
|
444
457
|
|
|
445
458
|
// Backfill order values for all entities
|
|
446
459
|
const handleBackfill = useCallback(async () => {
|
|
@@ -449,6 +462,9 @@ export function EntityCollectionBoardView<M extends Record<string, any> = any>({
|
|
|
449
462
|
console.log("No orderProperty, returning");
|
|
450
463
|
return;
|
|
451
464
|
}
|
|
465
|
+
analyticsController.onAnalyticsEvent?.("kanban_backfill_order", {
|
|
466
|
+
path: fullPath
|
|
467
|
+
});
|
|
452
468
|
setBackfillLoading(true);
|
|
453
469
|
|
|
454
470
|
try {
|
|
@@ -514,7 +530,7 @@ export function EntityCollectionBoardView<M extends Record<string, any> = any>({
|
|
|
514
530
|
} finally {
|
|
515
531
|
setBackfillLoading(false);
|
|
516
532
|
}
|
|
517
|
-
}, [orderProperty, fullPath, collection, dataSource, context, boardDataController]);
|
|
533
|
+
}, [orderProperty, fullPath, collection, dataSource, context, boardDataController, analyticsController]);
|
|
518
534
|
|
|
519
535
|
const handleEntityClick = useCallback((entity: Entity<M>) => {
|
|
520
536
|
onEntityClick?.(entity);
|
|
@@ -667,6 +683,10 @@ export function EntityCollectionBoardView<M extends Record<string, any> = any>({
|
|
|
667
683
|
columnLoadingState={columnLoadingState}
|
|
668
684
|
onLoadMoreColumn={(column) => boardDataController.loadMoreColumn(column)}
|
|
669
685
|
onAddItemToColumn={(column) => {
|
|
686
|
+
analyticsController.onAnalyticsEvent?.("kanban_new_entity_in_column", {
|
|
687
|
+
path: fullPath,
|
|
688
|
+
column
|
|
689
|
+
});
|
|
670
690
|
sideEntityController.open({
|
|
671
691
|
path: fullPath,
|
|
672
692
|
collection,
|
|
@@ -433,12 +433,17 @@ export const EntityCollectionView = React.memo(
|
|
|
433
433
|
|
|
434
434
|
// View mode change: update URL + save to local persistence
|
|
435
435
|
const onViewModeChange = useCallback((mode: ViewMode) => {
|
|
436
|
+
analyticsController.onAnalyticsEvent?.("view_mode_changed", {
|
|
437
|
+
path: fullPath,
|
|
438
|
+
from: viewMode,
|
|
439
|
+
to: mode
|
|
440
|
+
});
|
|
436
441
|
setViewMode(mode);
|
|
437
442
|
// Save to local persistence for next visit
|
|
438
443
|
if (userConfigPersistence) {
|
|
439
444
|
onCollectionModifiedForUser(fullPath, { defaultViewMode: mode } as PartialEntityCollection<M>);
|
|
440
445
|
}
|
|
441
|
-
}, [setViewMode, userConfigPersistence, onCollectionModifiedForUser, fullPath]);
|
|
446
|
+
}, [setViewMode, userConfigPersistence, onCollectionModifiedForUser, fullPath, analyticsController, viewMode]);
|
|
442
447
|
|
|
443
448
|
const createEnabled = canCreateEntity(collection, authController, fullPath, null);
|
|
444
449
|
|
|
@@ -568,12 +573,16 @@ export const EntityCollectionView = React.memo(
|
|
|
568
573
|
|
|
569
574
|
// Handle kanban property change
|
|
570
575
|
const onKanbanPropertyChange = useCallback((property: string) => {
|
|
576
|
+
analyticsController.onAnalyticsEvent?.("kanban_property_changed", {
|
|
577
|
+
path: fullPath,
|
|
578
|
+
property
|
|
579
|
+
});
|
|
571
580
|
setSelectedKanbanProperty(property);
|
|
572
581
|
// Save to local persistence
|
|
573
582
|
if (userConfigPersistence) {
|
|
574
583
|
onCollectionModifiedForUser(fullPath, { kanbanColumnProperty: property } as any);
|
|
575
584
|
}
|
|
576
|
-
}, [userConfigPersistence, onCollectionModifiedForUser, fullPath]);
|
|
585
|
+
}, [userConfigPersistence, onCollectionModifiedForUser, fullPath, analyticsController]);
|
|
577
586
|
|
|
578
587
|
const getPropertyFor = useCallback(({
|
|
579
588
|
propertyKey,
|
|
@@ -836,6 +845,24 @@ export const EntityCollectionView = React.memo(
|
|
|
836
845
|
/>
|
|
837
846
|
);
|
|
838
847
|
|
|
848
|
+
// Compute plugin-provided error view for collection loading errors
|
|
849
|
+
const pluginErrorView = useMemo(() => {
|
|
850
|
+
const error = tableController.dataLoadingError;
|
|
851
|
+
if (!error || !customizationController.plugins) return null;
|
|
852
|
+
for (const plugin of customizationController.plugins) {
|
|
853
|
+
if (plugin.collectionView?.CollectionError) {
|
|
854
|
+
const CollectionError = plugin.collectionView.CollectionError;
|
|
855
|
+
return <CollectionError
|
|
856
|
+
path={fullPath}
|
|
857
|
+
collection={collection}
|
|
858
|
+
parentCollectionIds={parentCollectionIds}
|
|
859
|
+
error={error}
|
|
860
|
+
/>;
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
return null;
|
|
864
|
+
}, [tableController.dataLoadingError, customizationController.plugins, fullPath, collection, parentCollectionIds]);
|
|
865
|
+
|
|
839
866
|
return (
|
|
840
867
|
<div className={cls("overflow-hidden h-full w-full rounded-md flex flex-col", className)}
|
|
841
868
|
ref={containerRef}>
|
|
@@ -872,7 +899,9 @@ export const EntityCollectionView = React.memo(
|
|
|
872
899
|
/>
|
|
873
900
|
|
|
874
901
|
{/* View content - only the view-specific content changes */}
|
|
875
|
-
{
|
|
902
|
+
{tableController.dataLoadingError && pluginErrorView
|
|
903
|
+
? pluginErrorView
|
|
904
|
+
: viewMode === "kanban" && enabledViews.includes("kanban") ? (
|
|
876
905
|
<EntityCollectionBoardView
|
|
877
906
|
key={`kanban-view-${fullPath}-${selectedKanbanProperty}`}
|
|
878
907
|
collection={collection}
|