@rebasepro/admin 0.0.1-canary.eae7889 → 0.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.
Files changed (197) hide show
  1. package/dist/{CollectionEditorDialog-B2M9lCyL.js → CollectionEditorDialog-MbvXGzEq.js} +42 -31
  2. package/dist/CollectionEditorDialog-MbvXGzEq.js.map +1 -0
  3. package/dist/{CollectionsStudioView-WG6soyfs.js → CollectionsStudioView-D9X6aiAr.js} +12 -12
  4. package/dist/CollectionsStudioView-D9X6aiAr.js.map +1 -0
  5. package/dist/{ContentHomePage-CDF_a6Lp.js → ContentHomePage-CfVB1eUo.js} +26 -26
  6. package/dist/ContentHomePage-CfVB1eUo.js.map +1 -0
  7. package/dist/{ExportCollectionAction-Dc0VOWMN.js → ExportCollectionAction-CUwJg4F9.js} +2 -2
  8. package/dist/{ExportCollectionAction-Dc0VOWMN.js.map → ExportCollectionAction-CUwJg4F9.js.map} +1 -1
  9. package/dist/{ImportCollectionAction-DpCagAOy.js → ImportCollectionAction-DGa_SF_8.js} +2 -2
  10. package/dist/{ImportCollectionAction-DpCagAOy.js.map → ImportCollectionAction-DGa_SF_8.js.map} +1 -1
  11. package/dist/{PropertyEditView-DS67DxoT.js → PropertyEditView-C4nlYmAc.js} +82 -104
  12. package/dist/PropertyEditView-C4nlYmAc.js.map +1 -0
  13. package/dist/{RolesView-CIuYBimF.js → RolesView-CNWxnR8e.js} +7 -5
  14. package/dist/RolesView-CNWxnR8e.js.map +1 -0
  15. package/dist/{UsersView-B5zelXnH.js → UsersView-YiTIcXkA.js} +14 -35
  16. package/dist/UsersView-YiTIcXkA.js.map +1 -0
  17. package/dist/collection_editor/ConfigControllerProvider.d.ts +0 -4
  18. package/dist/collection_editor/types/collection_editor_controller.d.ts +6 -3
  19. package/dist/collection_editor/types/config_controller.d.ts +14 -7
  20. package/dist/collection_editor/ui/AddKanbanColumnAction.d.ts +3 -2
  21. package/dist/collection_editor/ui/CollectionViewHeaderAction.d.ts +3 -2
  22. package/dist/collection_editor/ui/EditorCollectionAction.d.ts +1 -1
  23. package/dist/collection_editor/ui/EditorCollectionActionStart.d.ts +1 -1
  24. package/dist/collection_editor/ui/EditorEntityAction.d.ts +1 -1
  25. package/dist/collection_editor/ui/KanbanSetupAction.d.ts +3 -2
  26. package/dist/collection_editor/ui/PropertyAddColumnComponent.d.ts +3 -2
  27. package/dist/collection_editor/ui/collection_editor/CollectionDetailsForm.d.ts +3 -4
  28. package/dist/collection_editor/ui/collection_editor/CollectionEditorDialog.d.ts +2 -3
  29. package/dist/collection_editor/ui/collection_editor/CollectionPropertiesEditorForm.d.ts +1 -2
  30. package/dist/collection_editor/ui/collection_editor/SubcollectionsEditTab.d.ts +3 -2
  31. package/dist/collection_editor_ui.js +3 -3
  32. package/dist/components/EntityCollectionTable/EntityCollectionTable.d.ts +1 -1
  33. package/dist/components/EntityCollectionTable/EntityCollectionTableProps.d.ts +1 -1
  34. package/dist/components/EntityCollectionTable/column_utils.d.ts +2 -2
  35. package/dist/components/EntityCollectionTable/fields/TableMultipleRelationField.d.ts +1 -1
  36. package/dist/components/EntityCollectionTable/fields/TableReferenceField.d.ts +1 -1
  37. package/dist/components/EntityCollectionTable/fields/TableRelationField.d.ts +1 -1
  38. package/dist/components/EntityCollectionTable/fields/TableRelationSelectorField.d.ts +2 -2
  39. package/dist/components/EntityCollectionTable/internal/CollectionTableToolbar.d.ts +5 -1
  40. package/dist/components/EntityCollectionView/EntityCollectionBoardView.d.ts +3 -2
  41. package/dist/components/EntityCollectionView/EntityCollectionView.d.ts +2 -1
  42. package/dist/components/EntityCollectionView/EntityCollectionViewActions.d.ts +4 -2
  43. package/dist/components/EntityCollectionView/EntityCollectionViewStartActions.d.ts +4 -2
  44. package/dist/components/EntityCollectionView/FiltersDialog.d.ts +2 -2
  45. package/dist/components/EntityCollectionView/SplitListView.d.ts +3 -2
  46. package/dist/components/EntityEditView.d.ts +9 -2
  47. package/dist/components/RebaseCMS.d.ts +1 -1
  48. package/dist/components/ReferenceTable/EntitySelectionTable.d.ts +2 -2
  49. package/dist/components/ReferenceWidget.d.ts +2 -2
  50. package/dist/components/RelationSelector.d.ts +1 -1
  51. package/dist/components/SelectableTable/SelectableTable.d.ts +2 -2
  52. package/dist/editor.js +2 -2
  53. package/dist/editor.js.map +1 -1
  54. package/dist/hooks/navigation/useNavigationRegistry.d.ts +2 -1
  55. package/dist/hooks/useEntityHistory.d.ts +1 -1
  56. package/dist/{index-CHxgwt6E.js → index-CtzpHzMQ.js} +11 -4
  57. package/dist/index-CtzpHzMQ.js.map +1 -0
  58. package/dist/{index-Dey5WJpO.js → index-DKlrVD1m.js} +3 -3
  59. package/dist/index-DKlrVD1m.js.map +1 -0
  60. package/dist/{index-CBhrgpR7.js → index-kHJXfLNI.js} +3 -3
  61. package/dist/index-kHJXfLNI.js.map +1 -0
  62. package/dist/index.js +79 -63
  63. package/dist/index.js.map +1 -1
  64. package/dist/{useEntityHistory-Dcj4zhGj.js → useEntityHistory-UVsSclfZ.js} +3 -1
  65. package/dist/useEntityHistory-UVsSclfZ.js.map +1 -0
  66. package/dist/util/navigation_utils.d.ts +10 -1
  67. package/dist/{util-BQ82ySL3.js → util-CwLmSpGp.js} +1653 -1257
  68. package/dist/util-CwLmSpGp.js.map +1 -0
  69. package/package.json +9 -17
  70. package/src/collection_editor/ConfigControllerProvider.tsx +19 -28
  71. package/src/collection_editor/types/collection_editor_controller.tsx +3 -3
  72. package/src/collection_editor/types/config_controller.tsx +7 -7
  73. package/src/collection_editor/ui/AddKanbanColumnAction.tsx +4 -4
  74. package/src/collection_editor/ui/CollectionViewHeaderAction.tsx +3 -3
  75. package/src/collection_editor/ui/EditorCollectionAction.tsx +3 -3
  76. package/src/collection_editor/ui/EditorCollectionActionStart.tsx +7 -7
  77. package/src/collection_editor/ui/EditorEntityAction.tsx +3 -3
  78. package/src/collection_editor/ui/HomePageEditorCollectionAction.tsx +4 -2
  79. package/src/collection_editor/ui/KanbanSetupAction.tsx +4 -3
  80. package/src/collection_editor/ui/MissingReferenceWidget.tsx +3 -2
  81. package/src/collection_editor/ui/NewCollectionButton.tsx +2 -1
  82. package/src/collection_editor/ui/NewCollectionCard.tsx +2 -1
  83. package/src/collection_editor/ui/PropertyAddColumnComponent.tsx +3 -3
  84. package/src/collection_editor/ui/collection_editor/CollectionDetailsForm.tsx +5 -50
  85. package/src/collection_editor/ui/collection_editor/CollectionEditorDialog.tsx +12 -20
  86. package/src/collection_editor/ui/collection_editor/CollectionPropertiesEditorForm.tsx +1 -3
  87. package/src/collection_editor/ui/collection_editor/CollectionRLSTab.tsx +17 -2
  88. package/src/collection_editor/ui/collection_editor/CollectionRelationsTab.tsx +3 -3
  89. package/src/collection_editor/ui/collection_editor/CollectionStudioView.tsx +2 -1
  90. package/src/collection_editor/ui/collection_editor/CollectionsStudioView.tsx +18 -12
  91. package/src/collection_editor/ui/collection_editor/DisplaySettingsForm.tsx +1 -2
  92. package/src/collection_editor/ui/collection_editor/GetCodeDialog.tsx +1 -2
  93. package/src/collection_editor/ui/collection_editor/PropertyFieldPreview.tsx +6 -6
  94. package/src/collection_editor/ui/collection_editor/SubcollectionsEditTab.tsx +4 -4
  95. package/src/collection_editor/ui/collection_editor/properties/MapPropertyField.tsx +2 -2
  96. package/src/collection_editor/ui/collection_editor/properties/ReferencePropertyField.tsx +15 -49
  97. package/src/collection_editor/ui/collection_editor/properties/advanced/AdvancedPropertyValidation.tsx +4 -5
  98. package/src/collection_editor/ui/collection_editor/templates/pages_template.ts +1 -1
  99. package/src/collection_editor/ui/collection_editor/templates/products_template.ts +2 -2
  100. package/src/components/DefaultAppBar.tsx +2 -2
  101. package/src/components/DefaultDrawer.tsx +25 -17
  102. package/src/components/DrawerNavigationGroup.tsx +4 -4
  103. package/src/components/DrawerNavigationItem.tsx +6 -6
  104. package/src/components/EntityCollectionTable/EntityCollectionTable.tsx +4 -4
  105. package/src/components/EntityCollectionTable/EntityCollectionTableProps.tsx +1 -1
  106. package/src/components/EntityCollectionTable/PropertyTableCell.tsx +4 -4
  107. package/src/components/EntityCollectionTable/column_utils.tsx +3 -3
  108. package/src/components/EntityCollectionTable/fields/TableMultipleRelationField.tsx +3 -3
  109. package/src/components/EntityCollectionTable/fields/TableReferenceField.tsx +3 -3
  110. package/src/components/EntityCollectionTable/fields/TableRelationField.tsx +4 -4
  111. package/src/components/EntityCollectionTable/fields/TableRelationSelectorField.tsx +3 -3
  112. package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +8 -2
  113. package/src/components/EntityCollectionTable/internal/EntityTableCell.tsx +1 -1
  114. package/src/components/EntityCollectionTable/internal/common.tsx +5 -5
  115. package/src/components/EntityCollectionTable/internal/popup_field/PopupFormField.tsx +1 -1
  116. package/src/components/EntityCollectionTable/table_bindings.tsx +45 -35
  117. package/src/components/EntityCollectionView/EntityBoardCard.tsx +18 -19
  118. package/src/components/EntityCollectionView/EntityCard.tsx +2 -2
  119. package/src/components/EntityCollectionView/EntityCollectionBoardView.tsx +42 -14
  120. package/src/components/EntityCollectionView/EntityCollectionCardView.tsx +4 -3
  121. package/src/components/EntityCollectionView/EntityCollectionListView.tsx +157 -54
  122. package/src/components/EntityCollectionView/EntityCollectionView.tsx +169 -75
  123. package/src/components/EntityCollectionView/EntityCollectionViewActions.tsx +23 -13
  124. package/src/components/EntityCollectionView/EntityCollectionViewStartActions.tsx +21 -12
  125. package/src/components/EntityCollectionView/FiltersDialog.tsx +7 -7
  126. package/src/components/EntityCollectionView/SplitListView.tsx +24 -8
  127. package/src/components/EntityCollectionView/useEntityPreviewSlots.ts +33 -5
  128. package/src/components/EntityEditView.tsx +85 -85
  129. package/src/components/EntitySidePanel.tsx +18 -10
  130. package/src/components/HomePage/ContentHomePage.tsx +24 -15
  131. package/src/components/HomePage/NavigationCard.tsx +4 -4
  132. package/src/components/HomePage/NavigationGroup.tsx +2 -2
  133. package/src/components/RebaseAuthGate.tsx +2 -0
  134. package/src/components/RebaseCMS.tsx +4 -3
  135. package/src/components/RebaseNavigation.tsx +8 -5
  136. package/src/components/ReferenceTable/EntitySelectionTable.tsx +4 -4
  137. package/src/components/ReferenceWidget.tsx +3 -3
  138. package/src/components/RelationSelector.tsx +33 -5
  139. package/src/components/SelectableTable/SelectableTable.tsx +6 -6
  140. package/src/components/UserSelector.tsx +1 -1
  141. package/src/components/admin/RolesView.tsx +10 -3
  142. package/src/components/admin/UsersView.tsx +13 -25
  143. package/src/components/app/Scaffold.tsx +4 -4
  144. package/src/components/field_configs.tsx +29 -32
  145. package/src/components/history/EntityHistoryView.tsx +12 -1
  146. package/src/editor/editor.tsx +2 -2
  147. package/src/form/EntityForm.tsx +5 -4
  148. package/src/form/PropertyFieldBinding.tsx +14 -10
  149. package/src/form/components/FieldHelperText.tsx +1 -1
  150. package/src/form/field_bindings/ArrayCustomShapedFieldBinding.tsx +3 -3
  151. package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +5 -5
  152. package/src/form/field_bindings/BlockFieldBinding.tsx +4 -4
  153. package/src/form/field_bindings/DateTimeFieldBinding.tsx +1 -1
  154. package/src/form/field_bindings/KeyValueFieldBinding.tsx +1 -1
  155. package/src/form/field_bindings/MapFieldBinding.tsx +7 -7
  156. package/src/form/field_bindings/MarkdownEditorFieldBinding.tsx +1 -1
  157. package/src/form/field_bindings/MultipleRelationFieldBinding.tsx +3 -3
  158. package/src/form/field_bindings/ReferenceAsStringFieldBinding.tsx +7 -7
  159. package/src/form/field_bindings/ReferenceFieldBinding.tsx +2 -2
  160. package/src/form/field_bindings/RelationFieldBinding.tsx +4 -4
  161. package/src/form/field_bindings/RepeatFieldBinding.tsx +5 -5
  162. package/src/form/field_bindings/SelectFieldBinding.tsx +1 -1
  163. package/src/form/field_bindings/StorageUploadFieldBinding.tsx +1 -1
  164. package/src/form/field_bindings/SwitchFieldBinding.tsx +1 -1
  165. package/src/form/field_bindings/TextFieldBinding.tsx +7 -7
  166. package/src/form/field_bindings/UserSelectFieldBinding.tsx +1 -1
  167. package/src/form/useClearRestoreValue.tsx +1 -1
  168. package/src/form/validation.ts +1 -1
  169. package/src/hooks/navigation/contexts/CollectionRegistryContext.tsx +2 -1
  170. package/src/hooks/navigation/useBuildCollectionRegistryController.tsx +15 -3
  171. package/src/hooks/navigation/useNavigationRegistry.ts +14 -3
  172. package/src/hooks/navigation/useResolvedViews.tsx +1 -3
  173. package/src/hooks/navigation/useTopLevelNavigation.ts +1 -1
  174. package/src/hooks/navigation/utils.ts +1 -1
  175. package/src/hooks/useEntityHistory.ts +7 -2
  176. package/src/preview/PropertyPreview.tsx +27 -23
  177. package/src/preview/components/StorageThumbnail.tsx +4 -1
  178. package/src/preview/property_previews/ArrayOfMapsPreview.tsx +1 -1
  179. package/src/preview/property_previews/ArrayOfReferencesPreview.tsx +1 -1
  180. package/src/preview/property_previews/ArrayOfRelationsPreview.tsx +1 -1
  181. package/src/preview/property_previews/SkeletonPropertyComponent.tsx +3 -3
  182. package/src/preview/property_previews/StringPropertyPreview.tsx +3 -3
  183. package/src/routes/RebaseRoute.tsx +57 -11
  184. package/src/util/navigation_utils.ts +21 -2
  185. package/src/util/previews.ts +15 -6
  186. package/src/util/property_utils.tsx +3 -3
  187. package/dist/CollectionEditorDialog-B2M9lCyL.js.map +0 -1
  188. package/dist/CollectionsStudioView-WG6soyfs.js.map +0 -1
  189. package/dist/ContentHomePage-CDF_a6Lp.js.map +0 -1
  190. package/dist/PropertyEditView-DS67DxoT.js.map +0 -1
  191. package/dist/RolesView-CIuYBimF.js.map +0 -1
  192. package/dist/UsersView-B5zelXnH.js.map +0 -1
  193. package/dist/index-CBhrgpR7.js.map +0 -1
  194. package/dist/index-CHxgwt6E.js.map +0 -1
  195. package/dist/index-Dey5WJpO.js.map +0 -1
  196. package/dist/useEntityHistory-Dcj4zhGj.js.map +0 -1
  197. package/dist/util-BQ82ySL3.js.map +0 -1
@@ -19,9 +19,10 @@ import { SortableContext, verticalListSortingStrategy } from "@dnd-kit/sortable"
19
19
  import { restrictToVerticalAxis, restrictToWindowEdges } from "@dnd-kit/modifiers";
20
20
  import { RenameGroupDialog } from "./RenameGroupDialog";
21
21
  import { toArray } from "@rebasepro/utils";
22
- import { useCollapsedGroups, useCustomizationController, useTranslation, useSlot, useAdminModeController } from "@rebasepro/core";
22
+ import { useCollapsedGroups, buildCollapsedDefaults, useCustomizationController, useTranslation, useSlot, useAdminModeController, useRebaseRegistry } from "@rebasepro/core";
23
23
  import { useRestoreScroll } from "@rebasepro/core";
24
- import { STUDIO_NAVIGATION_GROUPS } from "@rebasepro/core";
24
+
25
+ import { BootstrapAdminBanner } from "@rebasepro/core";
25
26
  import { useBreadcrumbsController, useCMSContext } from "../../index";
26
27
 
27
28
  export const DEFAULT_GROUP_NAME = "Views";
@@ -45,7 +46,8 @@ export function ContentHomePage({
45
46
  const { navigationStateController } = context;
46
47
  const customizationController = useCustomizationController();
47
48
  const adminModeController = useAdminModeController();
48
- const isStudioMode = adminModeController.mode === "studio";
49
+ const registry = useRebaseRegistry();
50
+
49
51
  const { resolvedSlots } = customizationController;
50
52
  const breadcrumbs = useBreadcrumbsController();
51
53
  const { t } = useTranslation();
@@ -61,19 +63,19 @@ export function ContentHomePage({
61
63
  onNavigationEntriesUpdate = () => {}
62
64
  } = navigationStateController.topLevelNavigation || {};
63
65
 
66
+ // Studio mode shows view-type entries (devViews) + admin entries (Users/Roles).
67
+ // Content mode shows collections, admin entries (Users/Roles), and custom entries — but not studio views.
64
68
  const rawNavigationEntries = useMemo(() => {
65
- return unFilteredNavigationEntries.filter(e => {
66
- const isStudioGroup = e.group ? STUDIO_NAVIGATION_GROUPS.includes(e.group) : false;
67
- return isStudioMode ? isStudioGroup : !isStudioGroup;
68
- });
69
- }, [unFilteredNavigationEntries, isStudioMode]);
69
+ if (adminModeController.mode === "studio") {
70
+ return unFilteredNavigationEntries.filter(e => e.type === "view" || e.type === "admin");
71
+ }
72
+ return unFilteredNavigationEntries.filter(e => e.type !== "view");
73
+ }, [unFilteredNavigationEntries, adminModeController.mode]);
70
74
 
71
75
  const groupOrderFromNavController = useMemo(() => {
72
- return unFilteredGroupOrder.filter(g => {
73
- const isStudioGroup = STUDIO_NAVIGATION_GROUPS.includes(g);
74
- return isStudioMode ? isStudioGroup : !isStudioGroup;
75
- });
76
- }, [unFilteredGroupOrder, isStudioMode]);
76
+ const entryGroups = new Set(rawNavigationEntries.map(e => e.group).filter(Boolean));
77
+ return unFilteredGroupOrder.filter(g => entryGroups.has(g));
78
+ }, [unFilteredGroupOrder, rawNavigationEntries]);
77
79
 
78
80
  const fuse = useRef<Fuse<NavigationEntry> | null>(null);
79
81
  const [filteredUrls, setFilteredUrls] = useState<string[] | null>(null);
@@ -244,7 +246,11 @@ export function ContentHomePage({
244
246
  ...(adminGroupData ? [adminGroupData.name] : [])
245
247
  ], [items, adminGroupData]);
246
248
 
247
- const { isGroupCollapsed, toggleGroupCollapsed } = useCollapsedGroups(groupNames, "home");
249
+ const collapsedDefaults = useMemo(
250
+ () => buildCollapsedDefaults(registry.cmsConfig?.navigationGroupMappings, "home"),
251
+ [registry.cmsConfig?.navigationGroupMappings]
252
+ );
253
+ const { isGroupCollapsed, toggleGroupCollapsed } = useCollapsedGroups(groupNames, "home", collapsedDefaults);
248
254
 
249
255
  const {
250
256
  sensors,
@@ -312,8 +318,11 @@ export function ContentHomePage({
312
318
  Render
313
319
  ─────────────────────────────────────────────────────────────── */
314
320
  return (
315
- <div ref={containerRef} className="py-2 overflow-auto h-full w-full dark:bg-surface-800">
321
+ <div ref={containerRef} className="py-2 overflow-auto h-full w-full bg-surface-50 dark:bg-surface-800">
316
322
  <Container maxWidth="6xl">
323
+ <div className="mb-4">
324
+ <BootstrapAdminBanner />
325
+ </div>
317
326
  {/* search & actions */}
318
327
  <div
319
328
  className="w-full sticky py-4 transition-all duration-400 ease-in-out top-0 z-10 flex flex-row gap-4"
@@ -26,10 +26,10 @@ export const NavigationCard = React.memo(function NavigationCard({
26
26
  return (
27
27
  <Card
28
28
  className={cls(
29
- "group h-full p-5 cursor-pointer transition-all duration-200 ease-in-out",
30
- "border-surface-200/40 dark:border-surface-700/40",
31
- "hover:-translate-y-0.5 hover:shadow-md hover:shadow-primary/5",
32
- "hover:border-primary/30 dark:hover:border-primary/20",
29
+ "group h-full p-4 cursor-pointer transition-all duration-150 ease-in-out",
30
+ "border-surface-200 dark:border-surface-700/40",
31
+ "hover:shadow-md hover:shadow-black/[0.04]",
32
+ "hover:border-surface-300 dark:hover:border-primary/20",
33
33
  shrink && "w-full max-w-full min-h-0 scale-75"
34
34
  )}
35
35
  onClick={() => {
@@ -40,8 +40,8 @@ export function NavigationGroup({
40
40
  component={"h2"}
41
41
  color="secondary"
42
42
  className={cls(
43
- "p-4 py-2 rounded",
44
- "font-medium text-sm text-surface-600 dark:text-surface-400"
43
+ "px-4 py-1 rounded",
44
+ "font-semibold text-[11px] uppercase tracking-wider text-surface-400 dark:text-surface-400"
45
45
  )}
46
46
  >
47
47
  {currentGroupName}
@@ -27,6 +27,8 @@ export function RebaseAuthGate({ children }: { children: React.ReactNode }) {
27
27
  const registry = useRebaseRegistry();
28
28
  const authController = useAuthController();
29
29
 
30
+ console.log("[AuthGate] initialLoading:", authController?.initialLoading, "user:", authController?.user?.email ?? null);
31
+
30
32
  if (authController?.initialLoading) {
31
33
  return <CircularProgressCenter size={"large"}/>;
32
34
  }
@@ -10,7 +10,7 @@ import type { RebaseCMSConfig } from "@rebasepro/types";
10
10
  * is auto-wired as a native feature (slots, provider, Studio view) without
11
11
  * needing any external plugin.
12
12
  */
13
- export function RebaseCMS({ collections, homePage, entityViews, entityActions, plugins, collectionEditor }: RebaseCMSConfig) {
13
+ export function RebaseCMS({ collections, homePage, entityViews, entityActions, plugins, collectionEditor, navigationGroupMappings }: RebaseCMSConfig) {
14
14
  const dispatch = useRebaseRegistryDispatch();
15
15
 
16
16
  useLayoutEffect(() => {
@@ -19,9 +19,10 @@ homePage,
19
19
  entityViews,
20
20
  entityActions,
21
21
  plugins,
22
- collectionEditor });
22
+ collectionEditor,
23
+ navigationGroupMappings });
23
24
  return () => dispatch.unregisterCMS();
24
- }, [dispatch, collections, homePage, entityViews, entityActions, plugins, collectionEditor]);
25
+ }, [dispatch, collections, homePage, entityViews, entityActions, plugins, collectionEditor, navigationGroupMappings]);
25
26
 
26
27
  return null;
27
28
  }
@@ -69,11 +69,14 @@ export function RebaseNavigation({ children }: RebaseNavigationProps) {
69
69
  const userConfigPersistence = useBuildLocalConfigurationPersistence();
70
70
 
71
71
  // ── Collection Editor resolution ──────────────────────────────────
72
+ // The collection editor is ALWAYS enabled when Studio is registered.
73
+ // The `collectionEditor` CMS config is for fine-tuning (readOnly, auth, etc.),
74
+ // not for opting-in. When omitted, the editor defaults to enabled
75
+ // (read-only in production).
72
76
  const collectionEditorConfig = registry.cmsConfig?.collectionEditor;
73
- const collectionEditorEnabled = Boolean(collectionEditorConfig);
77
+ const collectionEditorEnabled = Boolean(collectionEditorConfig) || Boolean(registry.studioConfig);
74
78
  const collectionEditorOptions: CollectionEditorOptions | undefined = useMemo(() => {
75
- if (!collectionEditorConfig) return undefined;
76
- if (collectionEditorConfig === true) return {};
79
+ if (collectionEditorConfig === true || !collectionEditorConfig) return {};
77
80
  return collectionEditorConfig;
78
81
  }, [collectionEditorConfig]);
79
82
 
@@ -103,7 +106,7 @@ export function RebaseNavigation({ children }: RebaseNavigationProps) {
103
106
  rebaseClient,
104
107
  resolvedCollections,
105
108
  collectionEditorEnabled ? {
106
- readOnly: collectionEditorOptions?.readOnly,
109
+ readOnly: collectionEditorOptions?.readOnly ?? process.env.NODE_ENV === "production",
107
110
  getAuthToken: collectionEditorOptions?.getAuthToken ?? authController?.getAuthToken
108
111
  } : { readOnly: true }
109
112
  );
@@ -117,7 +120,6 @@ export function RebaseNavigation({ children }: RebaseNavigationProps) {
117
120
  return {
118
121
  slug: "schema",
119
122
  name: "Edit collections",
120
- group: "Database",
121
123
  icon: "LayoutList",
122
124
  nestedRoutes: true,
123
125
  view: (
@@ -138,6 +140,7 @@ export function RebaseNavigation({ children }: RebaseNavigationProps) {
138
140
  plugins: registry.cmsConfig?.plugins ?? EMPTY_PLUGINS,
139
141
  collections: collectionsBuilder,
140
142
  views: devViews,
143
+ navigationGroupMappings: registry.cmsConfig?.navigationGroupMappings,
141
144
  authController: context.authController!,
142
145
  data: context.data,
143
146
  collectionRegistryController,
@@ -72,7 +72,7 @@ export interface EntitySelectionProps<M extends Record<string, unknown>> {
72
72
  /**
73
73
  * Allow selection of entities that pass the given filter only.
74
74
  */
75
- forceFilter?: FilterValues<string>;
75
+ fixedFilter?: FilterValues<string>;
76
76
 
77
77
  /**
78
78
  * Use this description to indicate the user what to do in this dialog.
@@ -100,7 +100,7 @@ export function EntitySelectionTable<M extends Record<string, unknown>>(
100
100
  path: pathInput,
101
101
  selectedEntityIds: selectedEntityIdsProp,
102
102
  description,
103
- forceFilter,
103
+ fixedFilter,
104
104
  maxSelection
105
105
  }: EntitySelectionProps<M>) {
106
106
 
@@ -259,7 +259,7 @@ export function EntitySelectionTable<M extends Record<string, unknown>>(
259
259
  path,
260
260
  collection,
261
261
  entitiesDisplayedFirst,
262
- forceFilter,
262
+ fixedFilter,
263
263
  updateUrl: false
264
264
  });
265
265
 
@@ -288,7 +288,7 @@ export function EntitySelectionTable<M extends Record<string, unknown>>(
288
288
  </Typography>}
289
289
  defaultSize={collection.defaultSize}
290
290
  properties={collection.properties}
291
- forceFilter={forceFilter}
291
+ fixedFilter={fixedFilter}
292
292
  inlineEditing={false}
293
293
  selectionController={selectionController}
294
294
  actions={<EntitySelectionDialogActions
@@ -27,7 +27,7 @@ export type ReferenceWidgetProps<M extends Record<string, unknown>> = {
27
27
  /**
28
28
  * Allow selection of entities that pass the given filter only.
29
29
  */
30
- forceFilter?: FilterValues<string>;
30
+ fixedFilter?: FilterValues<string>;
31
31
  size: PreviewSize;
32
32
  className?: string;
33
33
  includeId?: boolean;
@@ -46,7 +46,7 @@ export function ReferenceWidget<M extends Record<string, unknown>>({
46
46
  onReferenceSelected,
47
47
  onMultipleReferenceSelected,
48
48
  previewProperties,
49
- forceFilter,
49
+ fixedFilter,
50
50
  size,
51
51
  className,
52
52
  includeId,
@@ -89,7 +89,7 @@ export function ReferenceWidget<M extends Record<string, unknown>>({
89
89
  collection,
90
90
  onSingleEntitySelected,
91
91
  onMultipleEntitiesSelected,
92
- forceFilter
92
+ fixedFilter
93
93
  }
94
94
  );
95
95
 
@@ -37,7 +37,7 @@ export interface RelationSelectorProps {
37
37
  invisible?: boolean;
38
38
 
39
39
  relation: Relation;
40
- forceFilter?: FilterValues<string>;
40
+ fixedFilter?: FilterValues<string>;
41
41
  pageSize?: number;
42
42
  emptyPlaceholder?: string;
43
43
  searchPlaceholder?: string;
@@ -60,7 +60,7 @@ export const RelationSelector = React.forwardRef<
60
60
  useChips = true,
61
61
  className,
62
62
  relation,
63
- forceFilter,
63
+ fixedFilter,
64
64
  pageSize,
65
65
  emptyPlaceholder,
66
66
  searchPlaceholder = "Search...",
@@ -83,6 +83,10 @@ export const RelationSelector = React.forwardRef<
83
83
  // Track IDs that were set via local user interaction (onItemClick / handleClear / handleRemoveItem).
84
84
  // When an incoming value change matches these IDs exactly, we skip async re-resolution.
85
85
  const localSelectionIdsRef = useRef<string | null>(null);
86
+ // Snapshot of selected IDs captured when the popover opens.
87
+ // Used to sort the dropdown list so selected items appear at the top.
88
+ // Stays stable for the entire popover session so items don't jump around.
89
+ const pinnedIdsRef = useRef<Set<string> | null>(null);
86
90
 
87
91
  const {
88
92
  items: availableItems,
@@ -94,7 +98,7 @@ export const RelationSelector = React.forwardRef<
94
98
  } = useRelationSelector({
95
99
  path: collection.slug,
96
100
  collection,
97
- forceFilter,
101
+ fixedFilter,
98
102
  pageSize
99
103
  });
100
104
 
@@ -323,6 +327,7 @@ relation } as RelationItem;
323
327
  newSelected = [item];
324
328
  setIsPopoverOpen(false);
325
329
  isPopoverOpenRef.current = false;
330
+ pinnedIdsRef.current = null;
326
331
  }
327
332
  setSelectedItems(newSelected);
328
333
  // Mark this fingerprint so the resolution effect skips async work
@@ -348,11 +353,13 @@ relation } as RelationItem;
348
353
  if (disabled) return;
349
354
  // We control open manually; only allow opening attempts from Radix (e.g. trigger press)
350
355
  if (next) {
356
+ // Capture current selection so we can pin those items to the top of the list
357
+ pinnedIdsRef.current = new Set(selectedItems.map(i => String(i.id)));
351
358
  setIsPopoverOpen(true);
352
359
  isPopoverOpenRef.current = true;
353
360
  }
354
361
  // Ignore close attempts here; outside click/Escape handled manually; single select closes explicitly on selection.
355
- }, [disabled]);
362
+ }, [disabled, selectedItems]);
356
363
 
357
364
  // Outside click + Escape handling (simple and reliable)
358
365
  useEffect(() => {
@@ -367,12 +374,14 @@ relation } as RelationItem;
367
374
  // Outside
368
375
  setIsPopoverOpen(false);
369
376
  isPopoverOpenRef.current = false;
377
+ pinnedIdsRef.current = null;
370
378
  }
371
379
 
372
380
  function handleKey(ev: KeyboardEvent) {
373
381
  if (ev.key === "Escape") {
374
382
  setIsPopoverOpen(false);
375
383
  isPopoverOpenRef.current = false;
384
+ pinnedIdsRef.current = null;
376
385
  }
377
386
  }
378
387
 
@@ -389,6 +398,7 @@ relation } as RelationItem;
389
398
  const closePopover = useCallback(() => {
390
399
  setIsPopoverOpen(false);
391
400
  isPopoverOpenRef.current = false;
401
+ pinnedIdsRef.current = null;
392
402
  }, []);
393
403
 
394
404
  const resolvedPlaceholder = placeholder || emptyPlaceholder || <EmptyValue className={"ml-2"}/>;
@@ -412,6 +422,11 @@ relation } as RelationItem;
412
422
  setIsPopoverOpen(o => {
413
423
  const next = !o;
414
424
  isPopoverOpenRef.current = next;
425
+ if (next) {
426
+ pinnedIdsRef.current = new Set(selectedItems.map(i => String(i.id)));
427
+ } else {
428
+ pinnedIdsRef.current = null;
429
+ }
415
430
  return next;
416
431
  });
417
432
  }}
@@ -589,7 +604,20 @@ relation } as RelationItem;
589
604
  </CommandPrimitive.Empty>
590
605
  )}
591
606
  <CommandPrimitive.Group>
592
- {availableItems.map((item) => {
607
+ {(() => {
608
+ // Sort items so that initially-selected (pinned) items appear first.
609
+ // We use the snapshot taken when the popover opened so items don't
610
+ // jump around as the user checks/unchecks.
611
+ const pinned = pinnedIdsRef.current;
612
+ const sortedItems = pinned && pinned.size > 0
613
+ ? [...availableItems].sort((a, b) => {
614
+ const aP = pinned.has(String(a.id)) ? 0 : 1;
615
+ const bP = pinned.has(String(b.id)) ? 0 : 1;
616
+ return aP - bP;
617
+ })
618
+ : availableItems;
619
+ return sortedItems;
620
+ })().map((item) => {
593
621
  const isSelected = selectedItems.some(v => String(v.id) === String(item.id));
594
622
  return (
595
623
  <CommandPrimitive.Item
@@ -66,7 +66,7 @@ export type SelectableTableProps<M extends Record<string, unknown>> = {
66
66
 
67
67
  inlineEditing?: boolean;
68
68
 
69
- forceFilter?: FilterValues<keyof M extends string ? keyof M : never>;
69
+ fixedFilter?: FilterValues<keyof M extends string ? keyof M : never>;
70
70
 
71
71
  highlightedRow?: (data: Entity<M>) => boolean;
72
72
 
@@ -158,7 +158,7 @@ export const SelectableTable = function SelectableTable<M extends Record<string,
158
158
  initialScroll,
159
159
  emptyComponent,
160
160
  columns,
161
- forceFilter,
161
+ fixedFilter,
162
162
  highlightedRow,
163
163
  endAdornment,
164
164
  AddColumnComponent,
@@ -225,8 +225,8 @@ export const SelectableTable = function SelectableTable<M extends Record<string,
225
225
 
226
226
  const onFilterUpdate = useCallback((updatedFilterValues?: FilterValues<any>) => {
227
227
  setFilterValues?.({ ...updatedFilterValues,
228
- ...forceFilter } as FilterValues<any>);
229
- }, [forceFilter]);
228
+ ...fixedFilter } as FilterValues<any>);
229
+ }, [fixedFilter]);
230
230
 
231
231
  const contextValue = useMemo(() => ({
232
232
  setPopupCell: setPopupCell as ((cell?: SelectedCellProps<M>) => void),
@@ -264,7 +264,7 @@ export const SelectableTable = function SelectableTable<M extends Record<string,
264
264
  checkFilterCombination={checkFilterCombination}
265
265
  createFilterField={filterable ? createFilterField : undefined}
266
266
  rowClassName={useCallback((entity: Entity<M>) => {
267
- return highlightedRow?.(entity) ? "bg-surface-50/75 bg-surface-100/75 dark:!bg-surface-800/60" : "";
267
+ return highlightedRow?.(entity) ? "bg-surface-accent-50 dark:!bg-surface-accent-950" : "";
268
268
  }, [highlightedRow])}
269
269
  className="grow"
270
270
  emptyComponent={emptyComponent}
@@ -312,7 +312,7 @@ function createFilterField({
312
312
  path={baseProperty.path}
313
313
  title={resolvedProperty?.name}
314
314
  includeId={baseProperty.includeId}
315
- previewProperties={baseProperty?.previewProperties}
315
+ previewProperties={baseProperty?.ui?.previewProperties}
316
316
  hidden={hidden}
317
317
  setHidden={setHidden}/>;
318
318
  } else if (baseProperty.type === "relation" && baseProperty.relation) {
@@ -42,7 +42,7 @@ export const UserSelector = React.forwardRef<
42
42
  clearable = true,
43
43
  className,
44
44
  pageSize,
45
- searchPlaceholder = "SearchIcon users...",
45
+ searchPlaceholder = "Search users...",
46
46
  noResultsText = "No users found.",
47
47
  loadingText = "Loading..."
48
48
  },
@@ -13,7 +13,7 @@ import { ConfirmationDialog } from "@rebasepro/core";
13
13
  // RolesView Component
14
14
  // ============================================
15
15
  export function RolesView({ userManagement }: { userManagement: UserManagementDelegate }) {
16
- const { roles, saveRole, deleteRole, loading, allowDefaultRolesCreation } = userManagement;
16
+ const { roles, saveRole, deleteRole, loading, allowDefaultRolesCreation, rolesError } = userManagement;
17
17
  const snackbarController = useSnackbarController();
18
18
  const { t } = useTranslation();
19
19
  const breadcrumbs = useBreadcrumbsController();
@@ -139,9 +139,16 @@ isAdmin: false }
139
139
  <TableCell colspan={3}>
140
140
  <CenteredView className="flex flex-col gap-4 my-8 items-center">
141
141
  <Typography variant="label">
142
- {t("no_roles_yet")}
142
+ {rolesError
143
+ ? t("no_permission_to_view_roles")
144
+ : t("no_roles_yet")}
143
145
  </Typography>
144
- {allowDefaultRolesCreation && saveRole && (
146
+ {rolesError && (
147
+ <Typography variant="caption" color="secondary">
148
+ {t("no_permission_description")}
149
+ </Typography>
150
+ )}
151
+ {!rolesError && allowDefaultRolesCreation && saveRole && (
145
152
  <Button onClick={createDefaultRoles}>
146
153
  {t("create_default_roles")}
147
154
  </Button>
@@ -7,7 +7,7 @@ import { Button, Container, Dialog, DialogActions, DialogContent, DialogTitle, I
7
7
  import { MailIcon, KeyRoundIcon, PlusIcon, Trash2Icon, CopyIcon, CheckCircleIcon, ChevronLeftIcon, ChevronRightIcon } from "lucide-react";
8
8
  import { RoleChip } from "./RoleChip";
9
9
  import { UserManagementDelegate, Role, UserCreationResult } from "@rebasepro/types";
10
- import { ConfirmationDialog } from "@rebasepro/core";
10
+ import { ConfirmationDialog, BootstrapAdminBanner } from "@rebasepro/core";
11
11
 
12
12
  const PAGE_SIZE = 25;
13
13
 
@@ -17,7 +17,7 @@ const PAGE_SIZE = 25;
17
17
  export function UsersView({ userManagement }: {
18
18
  userManagement: UserManagementDelegate;
19
19
  }) {
20
- const { roles, saveUser, createUser, deleteUser, resetPassword, loading: delegateLoading, bootstrapAdmin } = userManagement;
20
+ const { roles, saveUser, createUser, deleteUser, resetPassword, loading: delegateLoading, bootstrapAdmin, usersError } = userManagement;
21
21
  const snackbarController = useSnackbarController();
22
22
  const { user: loggedInUser } = useAuthController();
23
23
  const { t } = useTranslation();
@@ -247,28 +247,9 @@ message: error instanceof Error ? error.message : t("error_resetting_password")
247
247
  }
248
248
  };
249
249
 
250
- if (delegateLoading) {
251
- return <CenteredView><CircularProgress/></CenteredView>;
252
- }
253
-
254
250
  return (
255
251
  <Container className="w-full flex flex-col py-4 gap-4" maxWidth={"6xl"}>
256
- {/* Bootstrap warning when no admins */}
257
- {!hasAdmin && loggedInUser && bootstrapAdmin && (
258
- <div className="bg-yellow-100 dark:bg-yellow-900 border border-yellow-400 dark:border-yellow-700 rounded p-4 flex items-center justify-between">
259
- <div>
260
- <Typography variant="label" className="text-yellow-800 dark:text-yellow-200">
261
- {t("no_users_or_roles_defined")}
262
- </Typography>
263
- </div>
264
- <Button
265
- onClick={handleBootstrap}
266
- disabled={bootstrapping}
267
- >
268
- {bootstrapping ? <CircularProgress size="small"/> : t("add_logged_user_as_admin")}
269
- </Button>
270
- </div>
271
- )}
252
+ <BootstrapAdminBanner className="mb-4" />
272
253
 
273
254
  <div className="flex items-center mt-12 mb-4 gap-4">
274
255
  <Typography gutterBottom variant="h4" className="grow mb-0" component="h4">
@@ -310,7 +291,7 @@ message: error instanceof Error ? error.message : t("error_resetting_password")
310
291
  <TableCell header className="w-24 text-right">{t("actions")}</TableCell>
311
292
  </TableHeader>
312
293
  <TableBody>
313
- {tableLoading ? (
294
+ {(tableLoading || delegateLoading) ? (
314
295
  [
315
296
  { email: "w-48",
316
297
  name: "w-32",
@@ -394,13 +375,20 @@ roles: ["w-16", "w-16"] }
394
375
  </TableRow>
395
376
  )))}
396
377
 
397
- {displayUsers.length === 0 && !tableLoading && (
378
+ {displayUsers.length === 0 && !tableLoading && !delegateLoading && (
398
379
  <TableRow>
399
380
  <TableCell colspan={5}>
400
381
  <CenteredView className="flex flex-col gap-4 my-8 items-center">
401
382
  <Typography variant="label">
402
- {searchQuery ? t("no_users_found") : t("no_users_yet")}
383
+ {usersError
384
+ ? t("no_permission_to_view_users")
385
+ : searchQuery ? t("no_users_found") : t("no_users_yet")}
403
386
  </Typography>
387
+ {usersError && (
388
+ <Typography variant="caption" color="secondary">
389
+ {t("no_permission_description")}
390
+ </Typography>
391
+ )}
404
392
  </CenteredView>
405
393
  </TableCell>
406
394
  </TableRow>
@@ -137,7 +137,7 @@ export const Scaffold = React.memo<PropsWithChildren<ScaffoldProps>>(
137
137
  {hasAppBar && <DrawerHeader/>}
138
138
 
139
139
  <div
140
- className={cls(defaultBorderMixin, "bg-white dark:bg-surface-800", "grow overflow-auto m-0", {
140
+ className={cls(defaultBorderMixin, "bg-surface-50 dark:bg-surface-800", "grow overflow-auto m-0", {
141
141
  "lg:mt-4": !hasAppBar,
142
142
  "mt-1 lg:m-0 lg:mx-2 lg:mb-2 lg:rounded-lg lg:border-t lg:border-x lg:border-solid": padding,
143
143
  "border-t": hasAppBar && !padding
@@ -158,7 +158,7 @@ export const Scaffold = React.memo<PropsWithChildren<ScaffoldProps>>(
158
158
 
159
159
  const DrawerHeader = () => {
160
160
  return (
161
- <div className="flex flex-col min-h-16"></div>
161
+ <div className="flex flex-col min-h-14"></div>
162
162
  );
163
163
  };
164
164
 
@@ -184,8 +184,8 @@ function DrawerWrapper(props: {
184
184
  const { t } = useTranslation();
185
185
 
186
186
  const innerDrawer = <div
187
- className={cls("h-full no-scrollbar overflow-y-auto overflow-x-clip", defaultBorderMixin,
188
- isFloating ? `absolute top-0 left-0 bottom-0 z-50 bg-surface-50 ${darkBgFloating} shadow-2xl border-r` : `relative bg-surface-50 ${darkBg}`)}
187
+ className={cls("h-full overflow-hidden", defaultBorderMixin,
188
+ isFloating ? `absolute top-0 left-0 bottom-0 z-50 bg-surface-50 ${darkBgFloating} shadow-lg border-r` : `relative bg-surface-50 ${darkBg}`)}
189
189
  style={{
190
190
  width: visualWidth,
191
191
  transition: "left 75ms cubic-bezier(0.4, 0, 0.6, 1) 0ms, opacity 75ms cubic-bezier(0.4, 0, 0.6, 1) 0ms, width 75ms cubic-bezier(0.4, 0, 0.6, 1) 0ms"