@rebasepro/admin 0.0.1-canary.f81da60 → 0.1.2
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/{CollectionEditorDialog-D509-IMx.js → CollectionEditorDialog-ywdxhs1L.js} +18 -18
- package/dist/CollectionEditorDialog-ywdxhs1L.js.map +1 -0
- package/dist/{CollectionsStudioView-B549BDpU.js → CollectionsStudioView-BDzMFzqH.js} +4 -4
- package/dist/{CollectionsStudioView-B549BDpU.js.map → CollectionsStudioView-BDzMFzqH.js.map} +1 -1
- package/dist/{ContentHomePage--Bl1FXk7.js → ContentHomePage-0tHuEIm_.js} +26 -26
- package/dist/ContentHomePage-0tHuEIm_.js.map +1 -0
- package/dist/{ExportCollectionAction-CttNAdM1.js → ExportCollectionAction-BIrq92To.js} +2 -2
- package/dist/{ExportCollectionAction-CttNAdM1.js.map → ExportCollectionAction-BIrq92To.js.map} +1 -1
- package/dist/{ImportCollectionAction-BB33kxAN.js → ImportCollectionAction-h8yg_To8.js} +2 -2
- package/dist/{ImportCollectionAction-BB33kxAN.js.map → ImportCollectionAction-h8yg_To8.js.map} +1 -1
- package/dist/{PropertyEditView-UtDO8g0A.js → PropertyEditView-BuZrNnBN.js} +79 -101
- package/dist/PropertyEditView-BuZrNnBN.js.map +1 -0
- package/dist/{RolesView-B0E7L0hE.js → RolesView-CMPsaIXo.js} +2 -2
- package/dist/{RolesView-B0E7L0hE.js.map → RolesView-CMPsaIXo.js.map} +1 -1
- package/dist/{UsersView-BM2_7VPV.js → UsersView-BkeblMVT.js} +6 -28
- package/dist/UsersView-BkeblMVT.js.map +1 -0
- package/dist/collection_editor/ConfigControllerProvider.d.ts +0 -4
- package/dist/collection_editor/ui/collection_editor/CollectionDetailsForm.d.ts +1 -3
- package/dist/collection_editor/ui/collection_editor/CollectionEditorDialog.d.ts +0 -2
- package/dist/collection_editor/ui/collection_editor/CollectionPropertiesEditorForm.d.ts +1 -2
- package/dist/collection_editor_ui.js +3 -3
- package/dist/components/EntityCollectionTable/internal/CollectionTableToolbar.d.ts +5 -1
- package/dist/components/EntityCollectionView/EntityCollectionViewActions.d.ts +2 -1
- package/dist/components/EntityCollectionView/EntityCollectionViewStartActions.d.ts +2 -1
- package/dist/components/EntityEditView.d.ts +6 -0
- package/dist/components/RebaseCMS.d.ts +1 -1
- package/dist/{index-C9YDsMC9.js → index-BuZaHcyc.js} +3 -3
- package/dist/index-BuZaHcyc.js.map +1 -0
- package/dist/{index-CNDetux9.js → index-CS6uJ7oW.js} +2 -2
- package/dist/{index-CNDetux9.js.map → index-CS6uJ7oW.js.map} +1 -1
- package/dist/{index-DO7lMeNB.js → index-eRJbMvHi.js} +3 -3
- package/dist/index-eRJbMvHi.js.map +1 -0
- package/dist/index.js +18 -14
- package/dist/index.js.map +1 -1
- package/dist/util/navigation_utils.d.ts +10 -1
- package/dist/{util-DK1O3uM0.js → util-zfU1zOCX.js} +713 -603
- package/dist/util-zfU1zOCX.js.map +1 -0
- package/package.json +8 -8
- package/src/collection_editor/ConfigControllerProvider.tsx +1 -10
- package/src/collection_editor/ui/collection_editor/CollectionDetailsForm.tsx +3 -47
- package/src/collection_editor/ui/collection_editor/CollectionEditorDialog.tsx +2 -10
- package/src/collection_editor/ui/collection_editor/CollectionPropertiesEditorForm.tsx +1 -3
- package/src/collection_editor/ui/collection_editor/CollectionRelationsTab.tsx +3 -3
- package/src/collection_editor/ui/collection_editor/GetCodeDialog.tsx +0 -1
- package/src/collection_editor/ui/collection_editor/PropertyFieldPreview.tsx +6 -6
- package/src/collection_editor/ui/collection_editor/properties/MapPropertyField.tsx +1 -1
- package/src/collection_editor/ui/collection_editor/properties/ReferencePropertyField.tsx +15 -49
- package/src/collection_editor/ui/collection_editor/properties/advanced/AdvancedPropertyValidation.tsx +2 -3
- package/src/collection_editor/ui/collection_editor/templates/pages_template.ts +1 -1
- package/src/collection_editor/ui/collection_editor/templates/products_template.ts +2 -2
- package/src/components/DefaultAppBar.tsx +2 -2
- package/src/components/DefaultDrawer.tsx +25 -17
- package/src/components/DrawerNavigationGroup.tsx +4 -4
- package/src/components/DrawerNavigationItem.tsx +6 -6
- package/src/components/EntityCollectionTable/EntityCollectionRowActions.tsx +5 -3
- package/src/components/EntityCollectionTable/EntityCollectionTable.tsx +1 -1
- package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +8 -2
- package/src/components/EntityCollectionTable/internal/EntityTableCell.tsx +2 -2
- package/src/components/EntityCollectionTable/table_bindings.tsx +37 -27
- package/src/components/EntityCollectionView/EntityCard.tsx +2 -2
- package/src/components/EntityCollectionView/EntityCollectionCardView.tsx +4 -3
- package/src/components/EntityCollectionView/EntityCollectionListView.tsx +7 -6
- package/src/components/EntityCollectionView/EntityCollectionView.tsx +50 -7
- package/src/components/EntityCollectionView/EntityCollectionViewActions.tsx +17 -8
- package/src/components/EntityCollectionView/EntityCollectionViewStartActions.tsx +8 -4
- package/src/components/EntityCollectionView/useEntityPreviewSlots.ts +33 -5
- package/src/components/EntityEditView.tsx +80 -81
- package/src/components/EntitySidePanel.tsx +11 -7
- package/src/components/HomePage/ContentHomePage.tsx +24 -15
- package/src/components/HomePage/NavigationCard.tsx +4 -4
- package/src/components/HomePage/NavigationGroup.tsx +2 -2
- package/src/components/RebaseAuthGate.tsx +2 -0
- package/src/components/RebaseCMS.tsx +4 -3
- package/src/components/RebaseNavigation.tsx +7 -4
- package/src/components/RelationSelector.tsx +30 -2
- package/src/components/SelectableTable/SelectableTable.tsx +2 -2
- package/src/components/UserSelector.tsx +1 -1
- package/src/components/admin/UsersView.tsx +2 -17
- package/src/components/app/Scaffold.tsx +3 -3
- package/src/components/field_configs.tsx +3 -3
- package/src/form/PropertyFieldBinding.tsx +10 -6
- package/src/hooks/navigation/useResolvedViews.tsx +1 -3
- package/src/hooks/navigation/useTopLevelNavigation.ts +1 -1
- package/src/hooks/navigation/utils.ts +1 -1
- package/src/preview/PropertyPreview.tsx +17 -13
- package/src/routes/RebaseRoute.tsx +27 -2
- package/src/util/navigation_utils.ts +16 -2
- package/src/util/previews.ts +14 -5
- package/dist/CollectionEditorDialog-D509-IMx.js.map +0 -1
- package/dist/ContentHomePage--Bl1FXk7.js.map +0 -1
- package/dist/PropertyEditView-UtDO8g0A.js.map +0 -1
- package/dist/UsersView-BM2_7VPV.js.map +0 -1
- package/dist/index-C9YDsMC9.js.map +0 -1
- package/dist/index-DO7lMeNB.js.map +0 -1
- package/dist/util-DK1O3uM0.js.map +0 -1
|
@@ -22,7 +22,7 @@ export function DrawerNavigationItem({
|
|
|
22
22
|
}) {
|
|
23
23
|
|
|
24
24
|
const iconWrap = <div
|
|
25
|
-
className={"shrink-0 flex items-center justify-center w-[
|
|
25
|
+
className={"shrink-0 flex items-center justify-center w-[44px] h-[30px] text-surface-500 dark:text-text-secondary-dark [&>svg]:size-4"}>
|
|
26
26
|
{icon}
|
|
27
27
|
</div>;
|
|
28
28
|
|
|
@@ -33,12 +33,12 @@ export function DrawerNavigationItem({
|
|
|
33
33
|
width: "100%",
|
|
34
34
|
transition: drawerOpen ? "width 150ms ease-in" : undefined
|
|
35
35
|
}}
|
|
36
|
-
className={({ isActive }: any) => cls("rounded-
|
|
37
|
-
"hover:bg-surface-100 dark:hover:bg-surface-800/60 text-
|
|
36
|
+
className={({ isActive }: any) => cls("rounded-md truncate",
|
|
37
|
+
"hover:bg-surface-100 dark:hover:bg-surface-800/60 text-surface-700 dark:text-surface-300 hover:text-surface-900 dark:hover:text-white",
|
|
38
38
|
"flex flex-row items-center",
|
|
39
|
-
drawerOpen ? "pr-4 h-
|
|
40
|
-
"font-
|
|
41
|
-
isActive ? "bg-surface-
|
|
39
|
+
drawerOpen ? "pr-4 h-[30px]" : "h-[30px]",
|
|
40
|
+
"font-medium text-[13px]",
|
|
41
|
+
isActive ? "bg-surface-900/[0.06] dark:bg-surface-800/50 text-surface-900 dark:text-white" : ""
|
|
42
42
|
)}
|
|
43
43
|
to={url}
|
|
44
44
|
>
|
|
@@ -91,8 +91,10 @@ export const EntityCollectionRowActions = function EntityCollectionRowActions({
|
|
|
91
91
|
const content = (
|
|
92
92
|
<div
|
|
93
93
|
className={cls(
|
|
94
|
-
"h-full flex items-center justify-center flex-col
|
|
95
|
-
|
|
94
|
+
"h-full flex items-center justify-center flex-col z-10",
|
|
95
|
+
isSelected
|
|
96
|
+
? "bg-surface-accent-50 dark:bg-surface-accent-900"
|
|
97
|
+
: "bg-surface-50/90 dark:bg-surface-900/90",
|
|
96
98
|
frozen ? "sticky left-0" : ""
|
|
97
99
|
)}
|
|
98
100
|
onClick={useCallback((event: React.MouseEvent) => {
|
|
@@ -219,7 +221,7 @@ export const EntityCollectionRowActions = function EntityCollectionRowActions({
|
|
|
219
221
|
style={sortableStyle}
|
|
220
222
|
className={cls(
|
|
221
223
|
"flex-shrink-0",
|
|
222
|
-
frozen && "sticky left-0 z-10 bg-white dark:bg-surface-
|
|
224
|
+
frozen && "sticky left-0 z-10 bg-white dark:bg-surface-900"
|
|
223
225
|
)}
|
|
224
226
|
{...sortableAttrsWithoutTabIndex}
|
|
225
227
|
>
|
|
@@ -350,7 +350,7 @@ export const EntityCollectionTable = function EntityCollectionTable<M extends Re
|
|
|
350
350
|
|
|
351
351
|
<div ref={ref}
|
|
352
352
|
style={style}
|
|
353
|
-
className={cls("h-full w-full flex flex-col bg-white dark:bg-surface-
|
|
353
|
+
className={cls("h-full w-full flex flex-col bg-white dark:bg-surface-900", className)}>
|
|
354
354
|
|
|
355
355
|
{!hideToolbar && <CollectionTableToolbar
|
|
356
356
|
onTextSearch={onTextSearch}
|
|
@@ -13,6 +13,10 @@ interface CollectionTableToolbarProps {
|
|
|
13
13
|
viewModeToggle?: React.ReactNode;
|
|
14
14
|
title?: React.ReactNode,
|
|
15
15
|
onTextSearch?: (searchString?: string) => void;
|
|
16
|
+
/**
|
|
17
|
+
* Initial search string to pre-populate the search bar (e.g. from URL params).
|
|
18
|
+
*/
|
|
19
|
+
initialSearchText?: string;
|
|
16
20
|
/**
|
|
17
21
|
* When true the toolbar is in "compact" mode for the split-view left panel.
|
|
18
22
|
* - Search bar, loading spinner, and view-mode toggle are hidden.
|
|
@@ -29,6 +33,7 @@ export function CollectionTableToolbar({
|
|
|
29
33
|
onTextSearch,
|
|
30
34
|
title,
|
|
31
35
|
viewModeToggle,
|
|
36
|
+
initialSearchText,
|
|
32
37
|
compact = false
|
|
33
38
|
}: CollectionTableToolbarProps) {
|
|
34
39
|
|
|
@@ -54,7 +59,7 @@ export function CollectionTableToolbar({
|
|
|
54
59
|
{viewModeToggle}
|
|
55
60
|
</div>
|
|
56
61
|
|
|
57
|
-
{title && <div className={"
|
|
62
|
+
{title && <div className={"flex items-center"}>
|
|
58
63
|
{title}
|
|
59
64
|
</div>}
|
|
60
65
|
|
|
@@ -84,7 +89,8 @@ export function CollectionTableToolbar({
|
|
|
84
89
|
size={"small"}
|
|
85
90
|
placeholder={t("search")}
|
|
86
91
|
onTextSearch={onTextSearch}
|
|
87
|
-
expandable={true}
|
|
92
|
+
expandable={true}
|
|
93
|
+
initialValue={initialSearchText}/>}
|
|
88
94
|
</div>
|
|
89
95
|
|
|
90
96
|
{/* Secondary actions — always inline */}
|
|
@@ -204,7 +204,7 @@ export const EntityTableCell = React.memo<EntityTableCellProps>(
|
|
|
204
204
|
`flex relative h-full rounded-md p-${p} border-4`,
|
|
205
205
|
showSaved ? "bg-primary/20 dark:bg-primary/20" : (onHover && !disabled ? "bg-surface-50 dark:bg-surface-900" : ""),
|
|
206
206
|
hideOverflow ? "overflow-hidden" : "",
|
|
207
|
-
isSelected && !showSaved ? "bg-surface-50 dark:bg-surface-900" : "",
|
|
207
|
+
isSelected && !showSaved ? "bg-surface-accent-50 dark:bg-surface-accent-900" : "",
|
|
208
208
|
borderClass
|
|
209
209
|
)}
|
|
210
210
|
ref={ref}
|
|
@@ -265,7 +265,7 @@ export const EntityTableCell = React.memo<EntityTableCellProps>(
|
|
|
265
265
|
style={sortableStyle}
|
|
266
266
|
className={cls(
|
|
267
267
|
"flex-shrink-0",
|
|
268
|
-
frozen && "sticky left-0 z-10 bg-white dark:bg-surface-
|
|
268
|
+
frozen && "sticky left-0 z-10 bg-white dark:bg-surface-900"
|
|
269
269
|
)}
|
|
270
270
|
{...sortableAttrsWithoutTabIndex}
|
|
271
271
|
>
|
|
@@ -252,37 +252,12 @@ path: referenceProperty.path as string }) : undefined;
|
|
|
252
252
|
if (property.relation) {
|
|
253
253
|
if (property.ui?.widget === "dialog") {
|
|
254
254
|
return {
|
|
255
|
-
Component:
|
|
256
|
-
<TableRelationField
|
|
257
|
-
name={propertyKey}
|
|
258
|
-
internalValue={internalValue as EntityRelation}
|
|
259
|
-
updateValue={updateValue}
|
|
260
|
-
disabled={disabled}
|
|
261
|
-
size={size}
|
|
262
|
-
multiselect={false}
|
|
263
|
-
relation={property.relation}
|
|
264
|
-
previewProperties={property.ui?.previewProperties}
|
|
265
|
-
includeId={property.includeId}
|
|
266
|
-
includeEntityLink={property.includeEntityLink}
|
|
267
|
-
title={property.name ?? propertyKey}
|
|
268
|
-
fixedFilter={property.fixedFilter}
|
|
269
|
-
/>
|
|
270
|
-
),
|
|
255
|
+
Component: RelationDialogBindingComponent,
|
|
271
256
|
allowScroll: false
|
|
272
257
|
};
|
|
273
258
|
} else {
|
|
274
259
|
return {
|
|
275
|
-
Component:
|
|
276
|
-
<TableRelationSelectorField
|
|
277
|
-
name={propertyKey}
|
|
278
|
-
internalValue={internalValue as EntityRelation}
|
|
279
|
-
updateValue={updateValue}
|
|
280
|
-
disabled={disabled}
|
|
281
|
-
size={"small"}
|
|
282
|
-
relation={property.relation!}
|
|
283
|
-
fixedFilter={property.fixedFilter}
|
|
284
|
-
/>
|
|
285
|
-
),
|
|
260
|
+
Component: RelationSelectorBindingComponent,
|
|
286
261
|
allowScroll: false
|
|
287
262
|
};
|
|
288
263
|
}
|
|
@@ -344,3 +319,38 @@ path: referenceProperty.path as string }) : undefined;
|
|
|
344
319
|
|
|
345
320
|
return undefined;
|
|
346
321
|
}
|
|
322
|
+
|
|
323
|
+
/** Stable component for relation fields rendered with the dialog widget */
|
|
324
|
+
function RelationDialogBindingComponent({ propertyKey, internalValue, updateValue, disabled, size, property }: any) {
|
|
325
|
+
return (
|
|
326
|
+
<TableRelationField
|
|
327
|
+
name={propertyKey}
|
|
328
|
+
internalValue={internalValue as EntityRelation}
|
|
329
|
+
updateValue={updateValue}
|
|
330
|
+
disabled={disabled}
|
|
331
|
+
size={size}
|
|
332
|
+
multiselect={false}
|
|
333
|
+
relation={property.relation}
|
|
334
|
+
previewProperties={property.ui?.previewProperties}
|
|
335
|
+
includeId={property.includeId}
|
|
336
|
+
includeEntityLink={property.includeEntityLink}
|
|
337
|
+
title={property.name ?? propertyKey}
|
|
338
|
+
fixedFilter={property.fixedFilter}
|
|
339
|
+
/>
|
|
340
|
+
);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/** Stable component for relation fields rendered with the inline selector */
|
|
344
|
+
function RelationSelectorBindingComponent({ propertyKey, internalValue, updateValue, disabled, property }: any) {
|
|
345
|
+
return (
|
|
346
|
+
<TableRelationSelectorField
|
|
347
|
+
name={propertyKey}
|
|
348
|
+
internalValue={internalValue as EntityRelation}
|
|
349
|
+
updateValue={updateValue}
|
|
350
|
+
disabled={disabled}
|
|
351
|
+
size={"small"}
|
|
352
|
+
relation={property.relation!}
|
|
353
|
+
fixedFilter={property.fixedFilter}
|
|
354
|
+
/>
|
|
355
|
+
);
|
|
356
|
+
}
|
|
@@ -94,8 +94,8 @@ export function EntityCard<M extends Record<string, unknown> = Record<string, un
|
|
|
94
94
|
"cursor-pointer overflow-hidden group relative",
|
|
95
95
|
"transition-all duration-200",
|
|
96
96
|
"hover:shadow-lg hover:-translate-y-0.5",
|
|
97
|
-
selected && "ring-2 ring-primary",
|
|
98
|
-
highlighted && !selected && "ring-2 ring-primary ring-opacity-50"
|
|
97
|
+
selected && "ring-2 ring-primary bg-surface-accent-50 dark:bg-surface-accent-900",
|
|
98
|
+
highlighted && !selected && "ring-2 ring-primary ring-opacity-50 bg-surface-accent-50/50 dark:bg-surface-accent-900"
|
|
99
99
|
)}
|
|
100
100
|
onClick={handleClick}
|
|
101
101
|
>
|
|
@@ -5,7 +5,6 @@ import { EntityCard } from "./EntityCard";
|
|
|
5
5
|
import {
|
|
6
6
|
cls,
|
|
7
7
|
CircularProgress,
|
|
8
|
-
CircularProgressCenter,
|
|
9
8
|
Typography
|
|
10
9
|
} from "@rebasepro/ui";
|
|
11
10
|
import { useAuthController, useCustomizationController } from "@rebasepro/core";
|
|
@@ -213,9 +212,11 @@ export function EntityCollectionCardView<M extends Record<string, unknown> = Rec
|
|
|
213
212
|
</Typography>
|
|
214
213
|
</div>
|
|
215
214
|
) : isInitialLoading ? (
|
|
216
|
-
<
|
|
215
|
+
<div className="flex items-center justify-center py-12 px-8">
|
|
216
|
+
<CircularProgress size="small"/>
|
|
217
|
+
</div>
|
|
217
218
|
) : isEmpty ? (
|
|
218
|
-
<div className="
|
|
219
|
+
<div className="flex items-center justify-center py-12 px-8">
|
|
219
220
|
{emptyComponent ?? (
|
|
220
221
|
<Typography variant="label" color="secondary">
|
|
221
222
|
No entries found
|
|
@@ -7,7 +7,6 @@ import {
|
|
|
7
7
|
Checkbox,
|
|
8
8
|
Chip,
|
|
9
9
|
CircularProgress,
|
|
10
|
-
CircularProgressCenter,
|
|
11
10
|
cls,
|
|
12
11
|
defaultBorderMixin,
|
|
13
12
|
Typography
|
|
@@ -572,9 +571,11 @@ export function EntityCollectionListView<M extends Record<string, unknown> = Rec
|
|
|
572
571
|
</Typography>
|
|
573
572
|
</div>
|
|
574
573
|
) : isInitialLoading ? (
|
|
575
|
-
<
|
|
574
|
+
<div className="flex items-center justify-center py-12 px-8">
|
|
575
|
+
<CircularProgress size="small"/>
|
|
576
|
+
</div>
|
|
576
577
|
) : isEmpty ? (
|
|
577
|
-
<div className="flex items-center justify-center
|
|
578
|
+
<div className="flex items-center justify-center py-12 px-8">
|
|
578
579
|
{emptyComponent ?? (
|
|
579
580
|
<Typography variant="label" color="secondary">
|
|
580
581
|
No entries found
|
|
@@ -719,11 +720,11 @@ const ListRow = React.memo(function ListRow<M extends Record<string, unknown>>({
|
|
|
719
720
|
"flex items-center gap-4 cursor-pointer group transition-colors duration-200 relative h-full",
|
|
720
721
|
rowClasses,
|
|
721
722
|
isActive
|
|
722
|
-
? "bg-surface-accent-100 dark:bg-surface-accent-950 hover:bg-surface-accent-200 dark:hover:bg-surface-accent-
|
|
723
|
+
? "bg-surface-accent-100 dark:bg-surface-accent-950 hover:bg-surface-accent-200 dark:hover:bg-surface-accent-950"
|
|
723
724
|
: selected
|
|
724
|
-
? "bg-
|
|
725
|
+
? "bg-surface-accent-50 dark:bg-surface-accent-900 hover:bg-surface-accent-100 dark:hover:bg-surface-accent-950"
|
|
725
726
|
: highlighted
|
|
726
|
-
? "bg-surface-accent-50 dark:bg-surface-accent-
|
|
727
|
+
? "bg-surface-accent-50 dark:bg-surface-accent-900 hover:bg-surface-50 dark:hover:bg-surface-800/40"
|
|
727
728
|
: "bg-white dark:bg-surface-900 hover:bg-surface-50 dark:hover:bg-surface-800/40"
|
|
728
729
|
)}
|
|
729
730
|
onClick={handleClick}
|
|
@@ -30,7 +30,8 @@ import {
|
|
|
30
30
|
useLargeLayout,
|
|
31
31
|
usePermissions,
|
|
32
32
|
useTranslation,
|
|
33
|
-
useSlot
|
|
33
|
+
useSlot,
|
|
34
|
+
IconForView
|
|
34
35
|
} from "@rebasepro/core";
|
|
35
36
|
import { useUserConfigurationPersistence } from "@rebasepro/core";
|
|
36
37
|
import { EntityCollectionViewActions } from "./EntityCollectionViewActions";
|
|
@@ -373,6 +374,23 @@ export const EntityCollectionView = React.memo(
|
|
|
373
374
|
})
|
|
374
375
|
}, [path, sideEntityController]);
|
|
375
376
|
|
|
377
|
+
const openNewDocument = useCallback((defaultValues?: Record<string, unknown>) => {
|
|
378
|
+
const collection = collectionRef.current;
|
|
379
|
+
analyticsController.onAnalyticsEvent?.("new_entity_click", {
|
|
380
|
+
path: path
|
|
381
|
+
});
|
|
382
|
+
navigateToEntity({
|
|
383
|
+
openEntityMode,
|
|
384
|
+
collection,
|
|
385
|
+
entityId: undefined,
|
|
386
|
+
defaultValues,
|
|
387
|
+
path: path,
|
|
388
|
+
sideEntityController,
|
|
389
|
+
navigation: urlController,
|
|
390
|
+
onClose: unselectNavigatedEntity
|
|
391
|
+
});
|
|
392
|
+
}, [path, sideEntityController, openEntityMode, urlController, unselectNavigatedEntity]);
|
|
393
|
+
|
|
376
394
|
const onMultipleDeleteClick = () => {
|
|
377
395
|
analyticsController.onAnalyticsEvent?.("multiple_delete_dialog_open", {
|
|
378
396
|
path: path
|
|
@@ -837,7 +855,8 @@ export const EntityCollectionView = React.memo(
|
|
|
837
855
|
: null;
|
|
838
856
|
|
|
839
857
|
// Shared empty state — plugin slot takes priority, then default
|
|
840
|
-
const
|
|
858
|
+
const isSearching = !!tableController.searchString;
|
|
859
|
+
const isFilteredOrSorted = tableController.filterValues !== undefined || tableController.sortBy !== undefined || isSearching;
|
|
841
860
|
const emptyComponent = pluginEmptyStates.length > 0
|
|
842
861
|
? <>{pluginEmptyStates}</>
|
|
843
862
|
: canCreateEntities && !isFilteredOrSorted
|
|
@@ -851,13 +870,18 @@ export const EntityCollectionView = React.memo(
|
|
|
851
870
|
{t("create_your_first_entry")}
|
|
852
871
|
</Button>
|
|
853
872
|
</div>
|
|
854
|
-
: <Typography variant={"label"}>
|
|
873
|
+
: <Typography variant={"label"}>
|
|
874
|
+
{isSearching
|
|
875
|
+
? t("no_results_search", { search: tableController.searchString ?? "" })
|
|
876
|
+
: t("no_results_filter_sort")}
|
|
877
|
+
</Typography>;
|
|
855
878
|
|
|
856
879
|
const toolbarNode = (
|
|
857
880
|
<CollectionTableToolbar
|
|
858
881
|
compact={isCompact}
|
|
859
882
|
loading={tableController.dataLoading}
|
|
860
883
|
onTextSearch={tableController.setSearchString}
|
|
884
|
+
initialSearchText={tableController.searchString}
|
|
861
885
|
viewModeToggle={viewModeToggleElement}
|
|
862
886
|
actionsStart={<EntityCollectionViewStartActions
|
|
863
887
|
parentCollectionSlugs={parentCollectionSlugs ?? EMPTY_ARRAY} parentEntityIds={parentEntityIds ?? EMPTY_ARRAY}
|
|
@@ -868,6 +892,7 @@ export const EntityCollectionView = React.memo(
|
|
|
868
892
|
selectionController={usedSelectionController}
|
|
869
893
|
collectionEntitiesCount={docsCount ?? undefined}
|
|
870
894
|
resolvedProperties={resolvedCollection.properties}
|
|
895
|
+
openNewDocument={openNewDocument}
|
|
871
896
|
compact={isCompact}/>}
|
|
872
897
|
actions={
|
|
873
898
|
<EntityCollectionViewActions
|
|
@@ -876,6 +901,7 @@ export const EntityCollectionView = React.memo(
|
|
|
876
901
|
tableController={tableController}
|
|
877
902
|
onMultipleDeleteClick={onMultipleDeleteClick}
|
|
878
903
|
onNewClick={onNewClick}
|
|
904
|
+
openNewDocument={openNewDocument}
|
|
879
905
|
path={path}
|
|
880
906
|
relativePath={collection.slug}
|
|
881
907
|
selectionController={usedSelectionController}
|
|
@@ -1146,6 +1172,13 @@ export const EntityCollectionView = React.memo(
|
|
|
1146
1172
|
equal(a.selectedTab, b.selectedTab);
|
|
1147
1173
|
}) as React.FunctionComponent<EntityCollectionViewProps<any>>
|
|
1148
1174
|
|
|
1175
|
+
/**
|
|
1176
|
+
* Inflight count request deduplication map.
|
|
1177
|
+
* Keyed by `path|filterKey|sortByProperty|sortDir` so that concurrent
|
|
1178
|
+
* callers (e.g. React StrictMode double-mount) share the same promise.
|
|
1179
|
+
*/
|
|
1180
|
+
const inflightCountRequests = new Map<string, Promise<number>>();
|
|
1181
|
+
|
|
1149
1182
|
function EntitiesCount({
|
|
1150
1183
|
path,
|
|
1151
1184
|
collection,
|
|
@@ -1204,10 +1237,20 @@ function EntitiesCount({
|
|
|
1204
1237
|
const whereParams = Object.keys(whereMap).length > 0 ? whereMap : undefined;
|
|
1205
1238
|
const orderByParams = sortByProperty ? `${String(sortByProperty)}:${currentSort}` : undefined;
|
|
1206
1239
|
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1240
|
+
// Deduplicate inflight count requests (e.g. React StrictMode double-mount)
|
|
1241
|
+
const cacheKey = `${path}|${filterKey}|${sortByProperty ?? ""}|${currentSort ?? ""}`;
|
|
1242
|
+
let countPromise = inflightCountRequests.get(cacheKey);
|
|
1243
|
+
if (!countPromise) {
|
|
1244
|
+
countPromise = accessor.count({
|
|
1245
|
+
where: whereParams,
|
|
1246
|
+
orderBy: orderByParams
|
|
1247
|
+
});
|
|
1248
|
+
inflightCountRequests.set(cacheKey, countPromise);
|
|
1249
|
+
// Clean up the inflight entry once resolved/rejected
|
|
1250
|
+
countPromise.finally(() => inflightCountRequests.delete(cacheKey));
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
countPromise.then((c) => {
|
|
1211
1254
|
if (!cancelled) onCountChangeRef.current?.(c);
|
|
1212
1255
|
}).catch((e) => {
|
|
1213
1256
|
console.warn("Error fetching count", e);
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import type { EntityCollection } from "@rebasepro/types";
|
|
3
3
|
import React, { lazy, Suspense } from "react";
|
|
4
4
|
|
|
5
|
-
import { useLargeLayout, useTranslation, useSlot } from "@rebasepro/core";
|
|
5
|
+
import { useLargeLayout, useTranslation, useSlot, resolveComponentRef } from "@rebasepro/core";
|
|
6
6
|
import { CollectionActionsProps, EntityTableController, SelectionController } from "@rebasepro/types";
|
|
7
7
|
import { Button, IconButton, Tooltip, Popover, iconSize } from "@rebasepro/ui";
|
|
8
8
|
import { PlusIcon, Trash2Icon, MoreVerticalIcon } from "lucide-react";
|
|
@@ -29,6 +29,7 @@ export type EntityCollectionViewActionsProps<M extends Record<string, unknown>>
|
|
|
29
29
|
collectionEntitiesCount?: number;
|
|
30
30
|
compact?: boolean;
|
|
31
31
|
children?: React.ReactNode;
|
|
32
|
+
openNewDocument: (defaultValues?: Record<string, unknown>) => void;
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
export function EntityCollectionViewActions<M extends Record<string, unknown>>({
|
|
@@ -43,7 +44,8 @@ export function EntityCollectionViewActions<M extends Record<string, unknown>>({
|
|
|
43
44
|
tableController,
|
|
44
45
|
collectionEntitiesCount,
|
|
45
46
|
compact,
|
|
46
|
-
children
|
|
47
|
+
children,
|
|
48
|
+
openNewDocument
|
|
47
49
|
}: EntityCollectionViewActionsProps<M>) {
|
|
48
50
|
const context = useCMSContext();
|
|
49
51
|
|
|
@@ -116,15 +118,22 @@ export function EntityCollectionViewActions<M extends Record<string, unknown>>({
|
|
|
116
118
|
selectionController,
|
|
117
119
|
context,
|
|
118
120
|
tableController,
|
|
119
|
-
collectionEntitiesCount
|
|
121
|
+
collectionEntitiesCount,
|
|
122
|
+
openNewDocument
|
|
120
123
|
};
|
|
121
124
|
|
|
122
125
|
const actions = toArray(collection.Actions)
|
|
123
|
-
.map((
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
126
|
+
.map((actionRef, i) => {
|
|
127
|
+
const Action = resolveComponentRef(actionRef);
|
|
128
|
+
if (!Action) return null;
|
|
129
|
+
return (
|
|
130
|
+
<ErrorBoundary key={`actions_${i}`}>
|
|
131
|
+
<Suspense fallback={null}>
|
|
132
|
+
<Action {...actionProps}/>
|
|
133
|
+
</Suspense>
|
|
134
|
+
</ErrorBoundary>
|
|
135
|
+
);
|
|
136
|
+
});
|
|
128
137
|
|
|
129
138
|
const pluginActions = useSlot("collection.actions", actionProps as any);
|
|
130
139
|
|
|
@@ -25,6 +25,7 @@ export type EntityCollectionViewStartActionsProps<M extends Record<string, unkno
|
|
|
25
25
|
*/
|
|
26
26
|
resolvedProperties?: Properties;
|
|
27
27
|
compact?: boolean;
|
|
28
|
+
openNewDocument: (defaultValues?: Record<string, unknown>) => void;
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
export function EntityCollectionViewStartActions<M extends Record<string, unknown>>({
|
|
@@ -36,7 +37,8 @@ export function EntityCollectionViewStartActions<M extends Record<string, unknow
|
|
|
36
37
|
tableController,
|
|
37
38
|
collectionEntitiesCount,
|
|
38
39
|
resolvedProperties,
|
|
39
|
-
compact
|
|
40
|
+
compact,
|
|
41
|
+
openNewDocument
|
|
40
42
|
}: EntityCollectionViewStartActionsProps<M>) {
|
|
41
43
|
|
|
42
44
|
const context = useCMSContext();
|
|
@@ -64,7 +66,8 @@ export function EntityCollectionViewStartActions<M extends Record<string, unknow
|
|
|
64
66
|
selectionController,
|
|
65
67
|
context,
|
|
66
68
|
tableController,
|
|
67
|
-
collectionEntitiesCount
|
|
69
|
+
collectionEntitiesCount,
|
|
70
|
+
openNewDocument
|
|
68
71
|
};
|
|
69
72
|
|
|
70
73
|
const handleBackClick = useCallback(() => {
|
|
@@ -108,13 +111,14 @@ export function EntityCollectionViewStartActions<M extends Record<string, unknow
|
|
|
108
111
|
{t("filters")}{activeFilterCount > 0 ? ` (${activeFilterCount})` : ""}
|
|
109
112
|
</Button>
|
|
110
113
|
) : (
|
|
111
|
-
<
|
|
114
|
+
<Button
|
|
115
|
+
variant="text"
|
|
112
116
|
size="small"
|
|
113
117
|
onClick={() => setFiltersDialogOpen(true)}
|
|
114
118
|
className={cls(activeFilterCount > 0 && "text-primary")}
|
|
115
119
|
>
|
|
116
120
|
<FilterIcon size={iconSize.small}/>
|
|
117
|
-
</
|
|
121
|
+
</Button>
|
|
118
122
|
)}
|
|
119
123
|
</Badge>
|
|
120
124
|
</Tooltip>
|
|
@@ -110,11 +110,39 @@ export function resolveCollectionSlotKeys(
|
|
|
110
110
|
|
|
111
111
|
// Status: first string-enum that isn't the title
|
|
112
112
|
let statusKey: string | undefined;
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
113
|
+
|
|
114
|
+
// 1. Explicitly defined in previewProperties
|
|
115
|
+
if (!statusKey && collection.previewProperties) {
|
|
116
|
+
for (const key of collection.previewProperties) {
|
|
117
|
+
const p = collection.properties[key] as Property | undefined;
|
|
118
|
+
if (p?.type === "string" && "enum" in p && p.enum && key !== titleKey) {
|
|
119
|
+
statusKey = key;
|
|
120
|
+
break;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// 2. Explicitly defined in propertiesOrder
|
|
126
|
+
if (!statusKey && collection.propertiesOrder) {
|
|
127
|
+
for (const key of collection.propertiesOrder) {
|
|
128
|
+
if (typeof key === "string" && !key.startsWith("subcollection:")) {
|
|
129
|
+
const p = collection.properties[key] as Property | undefined;
|
|
130
|
+
if (p?.type === "string" && "enum" in p && p.enum && key !== titleKey) {
|
|
131
|
+
statusKey = key;
|
|
132
|
+
break;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// 3. Default automatic inference
|
|
139
|
+
if (!statusKey) {
|
|
140
|
+
for (const [key, prop] of Object.entries(collection.properties)) {
|
|
141
|
+
const p = prop as Property;
|
|
142
|
+
if (p.type === "string" && "enum" in p && p.enum && key !== titleKey) {
|
|
143
|
+
statusKey = key;
|
|
144
|
+
break;
|
|
145
|
+
}
|
|
118
146
|
}
|
|
119
147
|
}
|
|
120
148
|
|