@rebasepro/admin 0.2.4 → 0.2.5

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 (53) hide show
  1. package/dist/{CollectionEditorDialog-D0VqpLPO.js → CollectionEditorDialog-Cn8-tGyL.js} +22 -5
  2. package/dist/CollectionEditorDialog-Cn8-tGyL.js.map +1 -0
  3. package/dist/{CollectionsStudioView-Bc3Rxxc2.js → CollectionsStudioView-C-Ts1rZt.js} +4 -4
  4. package/dist/{CollectionsStudioView-Bc3Rxxc2.js.map → CollectionsStudioView-C-Ts1rZt.js.map} +1 -1
  5. package/dist/{ExportCollectionAction-Ckc-09BQ.js → ExportCollectionAction-BRdKM3DF.js} +2 -2
  6. package/dist/{ExportCollectionAction-Ckc-09BQ.js.map → ExportCollectionAction-BRdKM3DF.js.map} +1 -1
  7. package/dist/{ImportCollectionAction-BqjIrC3Z.js → ImportCollectionAction-U-v7lGxO.js} +2 -2
  8. package/dist/{ImportCollectionAction-BqjIrC3Z.js.map → ImportCollectionAction-U-v7lGxO.js.map} +1 -1
  9. package/dist/{PropertyEditView-CvRSV-A2.js → PropertyEditView-BDNYkfNf.js} +2 -2
  10. package/dist/{PropertyEditView-CvRSV-A2.js.map → PropertyEditView-BDNYkfNf.js.map} +1 -1
  11. package/dist/collection_editor_ui.js +3 -3
  12. package/dist/components/RebaseRouteDefs.d.ts +1 -1
  13. package/dist/components/admin/index.d.ts +1 -3
  14. package/dist/hooks/navigation/useBuildNavigationStateController.d.ts +1 -1
  15. package/dist/hooks/navigation/useResolvedViews.d.ts +2 -5
  16. package/dist/{index-DY2k5TtG.js → index-DHaOV-7A.js} +3 -3
  17. package/dist/index-DHaOV-7A.js.map +1 -0
  18. package/dist/{index-UQOMHwt1.js → index-DJSL_SCr.js} +3 -3
  19. package/dist/index-DJSL_SCr.js.map +1 -0
  20. package/dist/{index-BCcLwgfe.js → index-XMII4H3d.js} +2 -2
  21. package/dist/{index-BCcLwgfe.js.map → index-XMII4H3d.js.map} +1 -1
  22. package/dist/index.d.ts +0 -2
  23. package/dist/index.js +90 -295
  24. package/dist/index.js.map +1 -1
  25. package/dist/{util-ZM9gQuCv.js → util-0GYaJqL_.js} +153 -644
  26. package/dist/util-0GYaJqL_.js.map +1 -0
  27. package/package.json +8 -8
  28. package/src/collection_editor/pgColumnToProperty.ts +19 -2
  29. package/src/components/DefaultDrawer.tsx +2 -2
  30. package/src/components/EntityCollectionView/EntityCollectionCardView.tsx +4 -4
  31. package/src/components/EntityCollectionView/EntityCollectionListView.tsx +7 -0
  32. package/src/components/EntityCollectionView/EntityCollectionView.tsx +4 -1
  33. package/src/components/RebaseRouteDefs.tsx +4 -6
  34. package/src/components/admin/index.ts +1 -3
  35. package/src/components/index.ts +1 -3
  36. package/src/hooks/navigation/useBuildNavigationStateController.tsx +2 -3
  37. package/src/hooks/navigation/useResolvedViews.tsx +6 -48
  38. package/src/index.ts +2 -3
  39. package/src/util/previews.ts +9 -1
  40. package/dist/CollectionEditorDialog-D0VqpLPO.js.map +0 -1
  41. package/dist/components/admin/RoleChip.d.ts +0 -4
  42. package/dist/components/admin/RolesFilterSelect.d.ts +0 -2
  43. package/dist/components/admin/RolesView.d.ts +0 -4
  44. package/dist/components/admin/UserRolesSelectField.d.ts +0 -2
  45. package/dist/components/admin/UsersView.d.ts +0 -4
  46. package/dist/index-DY2k5TtG.js.map +0 -1
  47. package/dist/index-UQOMHwt1.js.map +0 -1
  48. package/dist/util-ZM9gQuCv.js.map +0 -1
  49. package/src/components/admin/RoleChip.tsx +0 -23
  50. package/src/components/admin/RolesFilterSelect.tsx +0 -45
  51. package/src/components/admin/RolesView.tsx +0 -470
  52. package/src/components/admin/UserRolesSelectField.tsx +0 -50
  53. package/src/components/admin/UsersView.tsx +0 -693
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@rebasepro/admin",
3
3
  "type": "module",
4
- "version": "0.2.4",
4
+ "version": "0.2.5",
5
5
  "description": "Rebase CMS — content management views, forms, and routing",
6
6
  "funding": {
7
7
  "url": "https://github.com/sponsors/rebaseco"
@@ -84,13 +84,13 @@
84
84
  "react-dropzone": "^14.4.1",
85
85
  "react-use-measure": "^2.1.7",
86
86
  "zod": "^3.25.76",
87
- "@rebasepro/common": "0.2.4",
88
- "@rebasepro/formex": "0.2.4",
89
- "@rebasepro/core": "0.2.4",
90
- "@rebasepro/schema-inference": "0.2.4",
91
- "@rebasepro/types": "0.2.4",
92
- "@rebasepro/utils": "0.2.4",
93
- "@rebasepro/ui": "0.2.4"
87
+ "@rebasepro/common": "0.2.5",
88
+ "@rebasepro/core": "0.2.5",
89
+ "@rebasepro/formex": "0.2.5",
90
+ "@rebasepro/schema-inference": "0.2.5",
91
+ "@rebasepro/utils": "0.2.5",
92
+ "@rebasepro/ui": "0.2.5",
93
+ "@rebasepro/types": "0.2.5"
94
94
  },
95
95
  "peerDependencies": {
96
96
  "react": ">=19.0.0",
@@ -158,12 +158,29 @@ label: prettifyIdentifier(v) })),
158
158
  };
159
159
 
160
160
  case "array":
161
- case "ARRAY":
161
+ case "ARRAY": {
162
+ let innerType = "string";
163
+ let colType: ArrayProperty["columnType"] = undefined;
164
+ if (udt_name === "_text" || udt_name === "_varchar") {
165
+ innerType = "string";
166
+ colType = "text[]";
167
+ } else if (udt_name === "_int4" || udt_name === "_int2" || udt_name === "_int8") {
168
+ innerType = "number";
169
+ colType = "integer[]";
170
+ } else if (udt_name === "_bool") {
171
+ innerType = "boolean";
172
+ colType = "boolean[]";
173
+ } else if (udt_name === "_numeric") {
174
+ innerType = "number";
175
+ colType = "numeric[]";
176
+ }
162
177
  return {
163
178
  type: "array",
164
179
  name: prettifiedName,
165
- of: { type: "string" }
180
+ columnType: colType,
181
+ of: { type: innerType }
166
182
  } as ArrayProperty;
183
+ }
167
184
 
168
185
  default:
169
186
  // Fallback: treat unknown types as string
@@ -317,7 +317,7 @@ export function DrawerToggle({
317
317
  const { t } = useTranslation();
318
318
 
319
319
  return (
320
- <div className="shrink-0 mt-auto px-2 py-2">
320
+ <div className="shrink-0 mt-auto px-4 pt-0.5 pb-2">
321
321
  <Tooltip
322
322
  title={isExpanded ? t("collapse") : t("expand")}
323
323
  side="right"
@@ -486,7 +486,7 @@ export function DrawerFooterActions({
486
486
  }
487
487
 
488
488
  return (
489
- <div className="shrink-0 pb-1" ref={portalRef}>
489
+ <div className="shrink-0 pt-2 pb-0" ref={portalRef}>
490
490
  {avatarComponent && (
491
491
  <div className="flex items-center px-[16px] py-1">
492
492
  <Menu
@@ -109,6 +109,8 @@ export function EntityCollectionCardView<M extends Record<string, unknown> = Rec
109
109
 
110
110
  // Infinite scroll with Intersection Observer — stable deps only
111
111
  useEffect(() => {
112
+ if (!loadMoreRef.current) return;
113
+
112
114
  const observer = new IntersectionObserver(
113
115
  (entries) => {
114
116
  const { paginationEnabled: pe, noMoreToLoad: nm, dataLoading: dl, itemCount: ic, pageSize: ps } = paginationStateRef.current;
@@ -124,12 +126,10 @@ export function EntityCollectionCardView<M extends Record<string, unknown> = Rec
124
126
  }
125
127
  );
126
128
 
127
- if (loadMoreRef.current) {
128
- observer.observe(loadMoreRef.current);
129
- }
129
+ observer.observe(loadMoreRef.current);
130
130
 
131
131
  return () => observer.disconnect();
132
- }, [setItemCount]);
132
+ }, [setItemCount, data.length]);
133
133
 
134
134
  // Scroll restoration — deferred to after layout paint
135
135
  useEffect(() => {
@@ -139,6 +139,13 @@ function getScrollParent(element: HTMLElement | null): HTMLElement | null {
139
139
  * PropertyPreview in list row columns (because it would blow up height).
140
140
  */
141
141
  function isComplexPropertyType(property: Property): boolean {
142
+ if (property.type === "array") {
143
+ const ofProp = "of" in property ? property.of : undefined;
144
+ const innerProp = ofProp ? (Array.isArray(ofProp) ? ofProp[0] : ofProp) : undefined;
145
+ if (innerProp && typeof innerProp === "object" && "enum" in innerProp && innerProp.enum) {
146
+ return false;
147
+ }
148
+ }
142
149
  return property.type === "array"
143
150
  || property.type === "map"
144
151
  || property.type === "reference"
@@ -1092,7 +1092,10 @@ export const EntityCollectionView = React.memo(
1092
1092
  ) : (
1093
1093
  <div className="flex flex-col w-full h-full">
1094
1094
  {toolbarNode}
1095
- <div className="flex-1 flex flex-col overflow-y-auto">
1095
+ <div className={cls(
1096
+ "flex-1 flex flex-col",
1097
+ viewMode === "list" && "overflow-y-auto"
1098
+ )}>
1096
1099
  {viewMode === "list" ? (
1097
1100
  <div
1098
1101
  className={cls(
@@ -5,13 +5,12 @@ import {
5
5
  RebaseRoutes,
6
6
  UserSettingsView,
7
7
  UIReferenceView,
8
- NotFoundPage,
9
- useInternalUserManagementController
8
+ NotFoundPage
10
9
  } from "@rebasepro/core";
11
10
  import { CircularProgressCenter } from "@rebasepro/ui";
12
11
 
13
12
  import { ContentHomePage } from "./HomePage/ContentHomePage";
14
- import { RolesView } from "./admin/RolesView";
13
+
15
14
  import { RebaseRoute } from "../routes/RebaseRoute";
16
15
  import { CustomViewRoute } from "../routes/CustomViewRoute";
17
16
  import { useNavigationStateController } from "../hooks/navigation/contexts/NavigationStateContext";
@@ -48,7 +47,7 @@ export interface RebaseRouteDefsProps {
48
47
  * Route definitions for the CMS.
49
48
  *
50
49
  * Defines all standard routes: home, studio home, collection view,
51
- * settings, users, roles, debug, custom views, and a catch-all 404.
50
+ * settings, debug, custom views, and a catch-all 404.
52
51
  *
53
52
  * **Independently usable**: Use this when you want Rebase routes
54
53
  * inside your own layout/navigation setup.
@@ -64,7 +63,6 @@ export interface RebaseRouteDefsProps {
64
63
  */
65
64
  export function RebaseRouteDefs({ children, layout }: RebaseRouteDefsProps) {
66
65
  const registry = useRebaseRegistry();
67
- const userManagement = useInternalUserManagementController();
68
66
  const navigationStateController = useNavigationStateController();
69
67
 
70
68
  const cmsHomePage = registry.cmsConfig?.homePage ?? <Suspense fallback={<CircularProgressCenter/>}><ContentHomePage/></Suspense>;
@@ -85,7 +83,7 @@ export function RebaseRouteDefs({ children, layout }: RebaseRouteDefsProps) {
85
83
 
86
84
  <Route path={"/c/*"} element={<RebaseRoute/>}/>
87
85
  <Route path={"/settings"} element={<SettingsView/>}/>
88
- {userManagement && <Route path={"/roles"} element={<RolesView userManagement={userManagement}/>}/>}
86
+
89
87
 
90
88
  {/* Hidden debug route */}
91
89
  <Route path={"/debug/ui"} element={<UIReferenceView/>}/>
@@ -1,3 +1 @@
1
- export * from "./UsersView";
2
- export * from "./RolesView";
3
- export * from "./RoleChip";
1
+ export * from "./CreationResultDialog";
@@ -32,9 +32,7 @@ export * from "./field_configs";
32
32
 
33
33
  export * from "./SideEntityProvider";
34
34
  export * from "./AdminModeSyncer";
35
- // Admin views are lazy-loaded by RebaseRouteDefs and useResolvedViews
36
- // export * from "./admin/RolesView";
37
- // export * from "./admin/UsersView";
35
+ // Admin views: only CreationResultDialog remains (used by resetPasswordAction)
38
36
  export * from "./app/AppBar";
39
37
  export * from "./app/Drawer";
40
38
  export * from "./app/Scaffold";
@@ -33,7 +33,7 @@ export type BuildNavigationStateProps<EC extends EntityCollection, USER extends
33
33
  * NavigationStateController. This is a thin composition of three focused hooks:
34
34
  *
35
35
  * - useResolvedCollections: resolves collection props and registers with CollectionRegistry
36
- * - useResolvedViews: resolves view/admin view props and injects Users/Roles views
36
+ * - useResolvedViews: resolves view/admin view props
37
37
  * - useTopLevelNavigation: computes the NavigationResult from resolved data
38
38
  *
39
39
  * The NavigationStateController type is preserved as a public API.
@@ -87,8 +87,7 @@ export function useBuildNavigationStateController<EC extends EntityCollection, U
87
87
  data,
88
88
  plugins,
89
89
  adminMode,
90
- effectiveRoleController,
91
- userManagement
90
+ effectiveRoleController
92
91
  });
93
92
 
94
93
  // Step 3: Compute top-level navigation (pure derived state)
@@ -1,6 +1,6 @@
1
1
 
2
- import type { AppView, AppViewsBuilder, EffectiveRoleController, EntityCollection, RebasePlugin } from "@rebasepro/types";
3
- import React, { lazy, Suspense, useCallback, useEffect, useMemo, useRef, useState } from "react";
2
+ import type { AppView, AppViewsBuilder, EffectiveRoleController, RebasePlugin } from "@rebasepro/types";
3
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
4
4
 
5
5
  /**
6
6
  * Compare two view arrays by their slug identity.
@@ -17,14 +17,10 @@ function viewSlugsEqual(a: AppView[] | undefined, b: AppView[] | undefined): boo
17
17
  return true;
18
18
  }
19
19
 
20
- import { AuthController, RebaseData, User } from "@rebasepro/types";
21
- import { UserManagementDelegate } from "@rebasepro/types";
20
+ import type { AuthController, RebaseData, User } from "@rebasepro/types";
22
21
 
23
22
  import { resolveAppViews } from "./useNavigationResolution";
24
23
 
25
-
26
- import { RolesView } from "../../components/admin/RolesView";
27
-
28
24
  export type UseResolvedViewsProps<USER extends User> = {
29
25
  authController: AuthController<USER>;
30
26
  views?: AppView[] | AppViewsBuilder;
@@ -33,7 +29,6 @@ export type UseResolvedViewsProps<USER extends User> = {
33
29
  plugins?: RebasePlugin[];
34
30
  adminMode?: "content" | "studio" | "settings";
35
31
  effectiveRoleController?: EffectiveRoleController;
36
- userManagement?: UserManagementDelegate<USER>;
37
32
  };
38
33
 
39
34
  export type UseResolvedViewsResult = {
@@ -46,8 +41,7 @@ export type UseResolvedViewsResult = {
46
41
 
47
42
  /**
48
43
  * Hook that resolves view and admin view props (which may be async builders or arrays)
49
- * into concrete AppView[]. Also injects the Roles admin view when userManagement
50
- * is provided with roles.
44
+ * into concrete AppView[].
51
45
  *
52
46
  * Uses refs for potentially-unstable dependencies (driver, authController,
53
47
  * plugins) to avoid re-triggering effects when their object identity changes.
@@ -63,8 +57,7 @@ export function useResolvedViews<USER extends User>(
63
57
  data,
64
58
  plugins,
65
59
  adminMode = "content",
66
- effectiveRoleController,
67
- userManagement
60
+ effectiveRoleController
68
61
  } = props;
69
62
 
70
63
  const [loading, setLoading] = useState(true);
@@ -110,32 +103,6 @@ export function useResolvedViews<USER extends User>(
110
103
  const resolvedAuthControllerRef = useRef(resolvedAuthController);
111
104
  resolvedAuthControllerRef.current = resolvedAuthController;
112
105
 
113
- // Memoize JSX element for injected Roles admin view to ensure stable reference.
114
- const hasRoles = !!userManagement?.roles;
115
- const rolesViewElement = useMemo(() =>
116
- hasRoles ? <RolesView /> : null,
117
- [hasRoles]
118
- );
119
-
120
- const injectedAdminViews: AppView[] = useMemo(() => {
121
- const views: AppView[] = [];
122
- const isUserAdmin = userManagement?.isAdmin !== false;
123
- if (userManagement && isUserAdmin && userManagement.roles && rolesViewElement) {
124
- views.push({
125
- slug: "roles",
126
- name: "Roles",
127
- icon: "Shield",
128
- view: rolesViewElement,
129
- group: "Settings"
130
- });
131
- }
132
- return views;
133
- }, [userManagement, rolesViewElement]);
134
-
135
- // Store injectedAdminViews in a ref for effect access
136
- const injectedAdminViewsRef = useRef(injectedAdminViews);
137
- injectedAdminViewsRef.current = injectedAdminViews;
138
-
139
106
  const initialLoading = resolvedAuthController.initialLoading;
140
107
  const user = resolvedAuthController.user;
141
108
 
@@ -146,20 +113,11 @@ export function useResolvedViews<USER extends User>(
146
113
 
147
114
  (async () => {
148
115
  try {
149
- const [newViews, newAdminViewsProp] = await Promise.all([
116
+ const [newViews, newAdminViews] = await Promise.all([
150
117
  resolveAppViews(viewsProp, resolvedAuthControllerRef.current, dataRef.current, pluginsRef.current),
151
118
  resolveAppViews(adminViewsProp, resolvedAuthControllerRef.current, dataRef.current)
152
119
  ]);
153
120
 
154
- // Generic dedup: developer-provided admin views override any injected ones with the same slug.
155
- // No hardcoded slug checks — works for any slug generically.
156
- const customSlugs = new Set(newAdminViewsProp.flatMap(v => Array.isArray(v.slug) ? v.slug : [v.slug]));
157
- const finalInjected = injectedAdminViewsRef.current.filter(v => {
158
- const slugs = Array.isArray(v.slug) ? v.slug : [v.slug];
159
- return slugs.every(s => !customSlugs.has(s));
160
- });
161
- const newAdminViews = [...newAdminViewsProp, ...finalInjected];
162
-
163
121
  // Compare views by slug identity rather than deepEqual.
164
122
  // Views contain React elements (JSX) whose internal properties
165
123
  // change on every render, making deepEqual unreliable.
package/src/index.ts CHANGED
@@ -45,7 +45,7 @@ export {
45
45
  DefaultDrawer,
46
46
  DrawerFooterActions,
47
47
  AdminModeSyncer,
48
- // ContentHomePage, UsersView, RolesView are lazy-loaded — not re-exported here
48
+ // ContentHomePage is lazy-loaded — not re-exported here
49
49
  RebaseCMS,
50
50
  RebaseShell,
51
51
  RebaseAuthGate,
@@ -97,7 +97,6 @@ export * from "./data_export";
97
97
  // Collection editor — moved from @rebasepro/studio (CMS-dependent visual schema editor)
98
98
  export * from "./collection_editor";
99
99
 
100
- export { RolesFilterSelect } from "./components/admin/RolesFilterSelect";
101
- export { UserRolesSelectField } from "./components/admin/UserRolesSelectField";
100
+
102
101
  export { CreationResultDialog } from "./components/admin/CreationResultDialog";
103
102
 
@@ -3,6 +3,11 @@ import { AuthController } from "@rebasepro/types";
3
3
  import { isPropertyBuilder } from "@rebasepro/common";
4
4
  import { isReferenceProperty, isRelationProperty } from "./property_utils";
5
5
 
6
+ function isHiddenProperty(property: Property | undefined): boolean {
7
+ if (!property) return false;
8
+ return Boolean(property.ui?.hideFromCollection);
9
+ }
10
+
6
11
  export function getEntityPreviewKeys(
7
12
  authController: AuthController,
8
13
  targetCollection: EntityCollection<any>,
@@ -26,7 +31,7 @@ export function getEntityPreviewKeys(
26
31
  })
27
32
  .filter(key => {
28
33
  const property = targetCollection.properties[key];
29
- return property && !isPropertyBuilder(property) && !isReferenceProperty(property) && !isRelationProperty(property);
34
+ return property && !isPropertyBuilder(property) && !isReferenceProperty(property) && !isRelationProperty(property) && !isHiddenProperty(property);
30
35
  }).slice(0, limit);
31
36
  }
32
37
  }
@@ -43,6 +48,9 @@ export function getEntityTitlePropertyKey<M extends Record<string, unknown>>(col
43
48
  const property = collection.properties[key];
44
49
  if (property && !isPropertyBuilder(property)) {
45
50
  const prop = property as Property;
51
+ if (isHiddenProperty(prop)) {
52
+ continue;
53
+ }
46
54
  if (prop.type === "string" && !prop.ui?.multiline && !prop.ui?.markdown && !prop.storage && !prop.isId) {
47
55
  if (!firstStringCandidate) {
48
56
  firstStringCandidate = key;