@rebasepro/admin 0.11.1-canary.gfd39654 → 0.12.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 (124) hide show
  1. package/README.md +1 -1
  2. package/dist/{CollectionEditorDialog-o7F_zbGY.js → CollectionEditorDialog-Dpc75wIZ.js} +56 -260
  3. package/dist/CollectionEditorDialog-Dpc75wIZ.js.map +1 -0
  4. package/dist/{PropertyEditView-DgB2b22q.js → PropertyEditView-DH3XZC2x.js} +2 -2
  5. package/dist/{PropertyEditView-DgB2b22q.js.map → PropertyEditView-DH3XZC2x.js.map} +1 -1
  6. package/dist/{RouterCollectionsStudioView-DaLRgPkj.js → RouterCollectionsStudioView-BZLq4MGp.js} +4 -4
  7. package/dist/{RouterCollectionsStudioView-DaLRgPkj.js.map → RouterCollectionsStudioView-BZLq4MGp.js.map} +1 -1
  8. package/dist/collection_editor/CollectionEditorDialogsContext.d.ts +1 -1
  9. package/dist/collection_editor/_cms_internals.d.ts +1 -1
  10. package/dist/collection_editor/extensibility_types.d.ts +1 -1
  11. package/dist/collection_editor/serializable_types.d.ts +40 -12
  12. package/dist/collection_editor/serializable_utils.d.ts +1 -1
  13. package/dist/collection_editor/ui/collection_editor/CollectionRLSTab.d.ts +1 -11
  14. package/dist/collection_editor_ui.js +4 -4
  15. package/dist/components/CollectionEditorDialogs.d.ts +2 -2
  16. package/dist/components/CollectionTableBinding/column_utils.d.ts +7 -1
  17. package/dist/components/CollectionViewBinding/CollectionListViewBinding.d.ts +2 -2
  18. package/dist/components/DefaultDrawer.d.ts +3 -3
  19. package/dist/components/DrawerNavigationGroup.d.ts +8 -1
  20. package/dist/components/DrawerNavigationItem.d.ts +7 -1
  21. package/dist/components/EditViewBinding.d.ts +1 -1
  22. package/dist/components/RebaseAdmin.d.ts +1 -1
  23. package/dist/components/RebaseAuthGate.d.ts +1 -1
  24. package/dist/components/RebaseLayout.d.ts +5 -1
  25. package/dist/components/RebaseNavigation.d.ts +2 -2
  26. package/dist/components/RebaseRouteDefs.d.ts +1 -1
  27. package/dist/components/RebaseShell.d.ts +6 -1
  28. package/dist/components/RelationSelector.d.ts +14 -0
  29. package/dist/components/SelectableTable/filters/RelationFilterField.d.ts +27 -0
  30. package/dist/components/SelectableTable/filters/null_filter.d.ts +46 -0
  31. package/dist/components/SidePanelProvider.d.ts +2 -2
  32. package/dist/components/app/Scaffold.d.ts +11 -0
  33. package/dist/components/field_configs.d.ts +1 -1
  34. package/dist/{export-BvqhjdEb.js → export-BzIpUdSK.js} +3 -3
  35. package/dist/export-BzIpUdSK.js.map +1 -0
  36. package/dist/form/EntityForm.d.ts +2 -2
  37. package/dist/form/EntityFormBinding.d.ts +5 -5
  38. package/dist/{history-Dz20hH38.js → history-g9688IaK.js} +2 -2
  39. package/dist/{history-Dz20hH38.js.map → history-g9688IaK.js.map} +1 -1
  40. package/dist/hooks/index.d.ts +1 -1
  41. package/dist/hooks/useAdminContext.d.ts +29 -0
  42. package/dist/hooks/useHistory.d.ts +7 -11
  43. package/dist/{import-DINSB-Vu.js → import-BjUG-cIY.js} +2 -2
  44. package/dist/{import-DINSB-Vu.js.map → import-BjUG-cIY.js.map} +1 -1
  45. package/dist/index.js +61 -38
  46. package/dist/index.js.map +1 -1
  47. package/dist/types/components/EntityFormProps.d.ts +1 -1
  48. package/dist/types/fields.d.ts +1 -1
  49. package/dist/{util-BhhfHY1M.js → util-B5fJm1FA.js} +296 -169
  50. package/dist/util-B5fJm1FA.js.map +1 -0
  51. package/package.json +9 -9
  52. package/src/collection_editor/CollectionEditorDialogsContext.tsx +1 -1
  53. package/src/collection_editor/ConfigControllerProvider.tsx +1 -1
  54. package/src/collection_editor/_cms_internals.ts +1 -1
  55. package/src/collection_editor/extensibility_types.ts +1 -1
  56. package/src/collection_editor/index.ts +2 -2
  57. package/src/collection_editor/serializable_types.ts +44 -13
  58. package/src/collection_editor/serializable_utils.ts +68 -59
  59. package/src/collection_editor/ui/collection_editor/CollectionEditorDialog.tsx +1 -1
  60. package/src/collection_editor/ui/collection_editor/CollectionRLSTab.tsx +66 -41
  61. package/src/collection_editor/ui/collection_editor/EntityActionsEditTab.tsx +1 -1
  62. package/src/collection_editor/ui/collection_editor/GetCodeDialog.tsx +1 -1
  63. package/src/collection_editor/ui/collection_editor/SubcollectionsEditTab.tsx +2 -2
  64. package/src/components/CollectionEditorDialogs.tsx +2 -2
  65. package/src/components/CollectionTableBinding/CollectionRowActions.tsx +5 -4
  66. package/src/components/CollectionTableBinding/CollectionTableBinding.tsx +8 -2
  67. package/src/components/CollectionTableBinding/column_utils.tsx +19 -5
  68. package/src/components/CollectionTableBinding/table_bindings.tsx +10 -10
  69. package/src/components/CollectionViewBinding/CollectionListViewBinding.tsx +8 -7
  70. package/src/components/CollectionViewBinding/CollectionViewActions.tsx +2 -2
  71. package/src/components/CollectionViewBinding/CollectionViewBinding.tsx +2 -2
  72. package/src/components/CollectionViewBinding/CollectionViewStartActions.tsx +2 -2
  73. package/src/components/DefaultDrawer.tsx +19 -8
  74. package/src/components/DeleteEntityDialog.tsx +2 -2
  75. package/src/components/DetailViewBinding.tsx +21 -1
  76. package/src/components/DrawerNavigationGroup.tsx +47 -4
  77. package/src/components/DrawerNavigationItem.tsx +19 -5
  78. package/src/components/EditFormActions.tsx +4 -3
  79. package/src/components/EditViewBinding.tsx +1 -1
  80. package/src/components/HomePage/ContentHomePage.tsx +4 -4
  81. package/src/components/RebaseAdmin.tsx +3 -3
  82. package/src/components/RebaseAuthGate.tsx +1 -1
  83. package/src/components/RebaseLayout.tsx +8 -2
  84. package/src/components/RebaseNavigation.tsx +9 -9
  85. package/src/components/RebaseRouteDefs.tsx +2 -2
  86. package/src/components/RebaseShell.tsx +10 -1
  87. package/src/components/RelationSelector.tsx +18 -3
  88. package/src/components/SelectableTable/filters/FilterFieldBinding.tsx +1 -1
  89. package/src/components/SelectableTable/filters/ReferenceFilterField.tsx +36 -9
  90. package/src/components/SelectableTable/filters/RelationFilterField.tsx +93 -17
  91. package/src/components/SelectableTable/filters/null_filter.ts +63 -0
  92. package/src/components/SidePanelProvider.tsx +2 -2
  93. package/src/components/app/Scaffold.tsx +16 -1
  94. package/src/data_export/export/ExportCollectionAction.tsx +2 -2
  95. package/src/form/EntityForm.tsx +2 -2
  96. package/src/form/EntityFormActions.tsx +2 -1
  97. package/src/form/EntityFormBinding.tsx +7 -7
  98. package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +3 -3
  99. package/src/form/field_bindings/MultipleRelationFieldBinding.tsx +16 -5
  100. package/src/form/field_bindings/ReferenceFieldBinding.tsx +3 -3
  101. package/src/form/field_bindings/RelationFieldBinding.tsx +17 -6
  102. package/src/form/field_bindings/RepeatFieldBinding.tsx +2 -2
  103. package/src/hooks/index.ts +1 -1
  104. package/src/hooks/navigation/useNavigationResolution.ts +10 -4
  105. package/src/hooks/{useCMSContext.tsx → useAdminContext.tsx} +10 -10
  106. package/src/hooks/useHistory.ts +7 -11
  107. package/src/hooks/useResolvedNavigationFrom.tsx +2 -2
  108. package/src/index.ts +4 -4
  109. package/src/preview/PropertyPreview.tsx +6 -6
  110. package/src/preview/components/RelationPreview.tsx +1 -1
  111. package/src/preview/property_previews/ArrayOfMapsPreview.tsx +1 -1
  112. package/src/preview/property_previews/ArrayOfReferencesPreview.tsx +2 -2
  113. package/src/preview/property_previews/ArrayOfRelationsPreview.tsx +2 -2
  114. package/src/preview/property_previews/SkeletonPropertyComponent.tsx +2 -2
  115. package/src/types/components/EntityFormProps.tsx +2 -2
  116. package/src/types/fields.tsx +1 -1
  117. package/dist/CollectionEditorDialog-o7F_zbGY.js.map +0 -1
  118. package/dist/collection_editor/pgColumnToProperty.d.ts +0 -7
  119. package/dist/editor/extensions/Image/index.d.ts +0 -6
  120. package/dist/export-BvqhjdEb.js.map +0 -1
  121. package/dist/hooks/useCMSContext.d.ts +0 -29
  122. package/dist/util-BhhfHY1M.js.map +0 -1
  123. package/src/collection_editor/pgColumnToProperty.ts +0 -291
  124. package/src/editor/extensions/Image/index.ts +0 -133
@@ -18,6 +18,18 @@ export interface ScaffoldProps {
18
18
  */
19
19
  autoOpenDrawer?: boolean;
20
20
 
21
+ /**
22
+ * Start with the drawer expanded rather than collapsed to icons.
23
+ *
24
+ * Distinct from {@link autoOpenDrawer}, which is a hover behaviour: this one
25
+ * only seeds the initial state, so the user's own toggle still wins for the
26
+ * rest of the session.
27
+ *
28
+ * Ignored on small layouts, where an expanded drawer covers the content it is
29
+ * meant to navigate.
30
+ */
31
+ defaultDrawerOpen?: boolean;
32
+
21
33
  /**
22
34
  * Logo to be displayed in the top bar and drawer.
23
35
  * Note that this has no effect if you are using a custom AppBar or Drawer.
@@ -51,6 +63,7 @@ export const Scaffold = React.memo<PropsWithChildren<ScaffoldProps>>(
51
63
  const {
52
64
  children,
53
65
  autoOpenDrawer,
66
+ defaultDrawerOpen = false,
54
67
  logo,
55
68
  className,
56
69
  style,
@@ -70,7 +83,9 @@ export const Scaffold = React.memo<PropsWithChildren<ScaffoldProps>>(
70
83
  const includeDrawer = drawerChildren.length > 0;
71
84
  const largeLayout = useLargeLayout();
72
85
 
73
- const [drawerOpen, setDrawerOpen] = React.useState(false);
86
+ // Seeded once, deliberately: a `useEffect` syncing this to the prop would
87
+ // re-expand the drawer under a user who had just collapsed it.
88
+ const [drawerOpen, setDrawerOpen] = React.useState(defaultDrawerOpen && largeLayout);
74
89
  const [onHover, setOnHover] = React.useState(false);
75
90
 
76
91
  const setOnHoverTrue = useCallback(() => setOnHover(true), []);
@@ -7,7 +7,7 @@ import {
7
7
  useData,
8
8
  useTranslation
9
9
  } from "@rebasepro/app";
10
- import { useCMSContext } from "../../hooks";
10
+ import { useAdminContext } from "../../hooks";
11
11
  import { Entity, User } from "@rebasepro/types";
12
12
  import { CollectionActionsProps, ExportConfig, RebaseContext, AdminCollection } from "@rebasepro/admin-types";
13
13
  import { getDefaultValuesFor } from "@rebasepro/common";
@@ -56,7 +56,7 @@ export function ExportCollectionAction<M extends Record<string, unknown>, USER e
56
56
  const [exportType, setExportType] = React.useState<"csv" | "json">("csv");
57
57
  const [dateExportType, setDateExportType] = React.useState<"timestamp" | "string">("string");
58
58
 
59
- const context = useCMSContext<USER>();
59
+ const context = useAdminContext<USER>();
60
60
  const dataClient = useData();
61
61
 
62
62
  const canExport = !exportAllowed || exportAllowed({
@@ -39,9 +39,9 @@ import {
39
39
  /**
40
40
  * Headless entity form component.
41
41
  *
42
- * Renders a form for a entity collection without any CMS or backend dependencies.
42
+ * Renders a form for a entity collection without any admin or backend dependencies.
43
43
  * All backend concerns (save, caching, analytics, plugin slots) are provided via
44
- * callback props. For CMS-connected usage, use {@link EntityFormBinding} instead.
44
+ * callback props. For admin-connected usage, use {@link EntityFormBinding} instead.
45
45
  *
46
46
  * @group Components
47
47
  */
@@ -5,6 +5,7 @@ import type { EntityAction, AdminCollection } from "@rebasepro/admin-types";
5
5
  import React from "react";
6
6
  import { Entity } from "@rebasepro/types";
7
7
  import type { EntityFormActionsProps } from "../types/components/EntityFormActionsProps";
8
+ import { getIcon } from "@rebasepro/app";
8
9
  import {
9
10
  AlertCircleIcon,
10
11
  Button,
@@ -136,7 +137,7 @@ function buildBottomActions<M extends Record<string, unknown>>({
136
137
  formContext
137
138
  });
138
139
  }}>
139
- {action.icon}
140
+ {getIcon(action.icon, undefined, undefined, "smallest")}
140
141
  </IconButton>
141
142
  ))}
142
143
  </div>}
@@ -30,7 +30,7 @@ import {
30
30
 
31
31
  import { useCollectionRegistryController } from "../hooks/navigation/contexts/CollectionRegistryContext";
32
32
  import { useSidePanel } from "../hooks/useSidePanel";
33
- import { useCMSContext } from "../hooks/useCMSContext";
33
+ import { useAdminContext } from "../hooks/useAdminContext";
34
34
  import { EntityForm } from "./EntityForm";
35
35
  import {
36
36
  extractTouchedValues,
@@ -41,9 +41,9 @@ import {
41
41
  import { mergeDeep } from "@rebasepro/utils";
42
42
 
43
43
  /**
44
- * Props for the CMS-connected EntityFormBinding wrapper.
44
+ * Props for the admin-connected EntityFormBinding wrapper.
45
45
  * Extends EntityFormProps — you can pass all the same props as EntityForm,
46
- * but the binding provides CMS context (save, caching, analytics, plugin slots)
46
+ * but the binding provides admin context (save, caching, analytics, plugin slots)
47
47
  * automatically.
48
48
  */
49
49
  export type EntityFormBindingProps<M extends Record<string, unknown>> = Omit<EntityFormProps<M>,
@@ -63,7 +63,7 @@ export type EntityFormBindingProps<M extends Record<string, unknown>> = Omit<Ent
63
63
  >;
64
64
 
65
65
  /**
66
- * CMS-connected wrapper around EntityForm.
66
+ * admin-connected wrapper around EntityForm.
67
67
  *
68
68
  * Provides all backend concerns that the headless EntityForm delegates via callbacks:
69
69
  * - Save via the data layer (`saveEntityWithCallbacks`)
@@ -74,8 +74,8 @@ export type EntityFormBindingProps<M extends Record<string, unknown>> = Omit<Ent
74
74
  * - Analytics events
75
75
  * - Navigation via side entity controller
76
76
  *
77
- * Use this component inside the CMS (`<RebaseShell>`).
78
- * For headless usage outside the CMS, use `EntityForm` directly.
77
+ * Use this component inside the admin (`<RebaseShell>`).
78
+ * For headless usage outside the admin, use `EntityForm` directly.
79
79
  *
80
80
  * @group Components
81
81
  */
@@ -102,7 +102,7 @@ export function EntityFormBinding<M extends Record<string, unknown>>({
102
102
  const dataClient = useData();
103
103
  const snackbarController = useSnackbarController();
104
104
  const customizationController = useCustomizationController();
105
- const context = useCMSContext();
105
+ const context = useAdminContext();
106
106
  const analyticsController = useAnalyticsController();
107
107
 
108
108
  const [status, setStatus] = useState<EntityStatus>(initialStatus);
@@ -77,7 +77,7 @@ export function ArrayOfReferencesFieldBinding({
77
77
  collection,
78
78
  onMultipleEntitiesSelected,
79
79
  selectedEntityIds,
80
- fixedFilter: ofProperty.fixedFilter
80
+ fixedFilter: ofProperty.admin?.fixedFilter
81
81
  }
82
82
  );
83
83
 
@@ -104,8 +104,8 @@ export function ArrayOfReferencesFieldBinding({
104
104
  onClick={onEntryClick}
105
105
  hover={!disabled}
106
106
  reference={entryValue}
107
- includeId={ofProperty.includeId}
108
- includeEntityLink={ofProperty.includeEntityLink}
107
+ includeId={ofProperty.admin?.includeId}
108
+ includeEntityLink={ofProperty.admin?.includeEntityLink}
109
109
  />
110
110
  );
111
111
  }, [ofProperty.path, ofProperty.admin?.previewProperties, value]);
@@ -43,8 +43,19 @@ export function MultipleRelationFieldBinding({
43
43
 
44
44
  const parentCollection = context.collection;
45
45
  const capabilities = parentCollection ? getDataSourceCapabilities(parentCollection.engine) : undefined;
46
- if (!parentCollection || !capabilities?.supportsRelations || !("relations" in parentCollection) || !parentCollection.relations) {
47
- throw Error("RelationFieldBinding expected a collection with relations support");
46
+ // Only two things have to hold here: there is a collection, and its engine
47
+ // has relations at all. Whether *this* property's relation can be resolved
48
+ // is `resolveRelationProperty`'s question, and it answers it across all
49
+ // three forms — a pre-stamped `resolvedRelation`, an inline `relation`, or
50
+ // a lookup by name in the collection's `relations` array — with an error
51
+ // naming the property and the collection when it cannot.
52
+ //
53
+ // This used to demand the `relations` array as well, which the inline form
54
+ // does not produce. That is the documented way to declare a relation, so
55
+ // every collection using it threw here and rendered the error boundary
56
+ // instead of the field.
57
+ if (!parentCollection || !capabilities?.supportsRelations) {
58
+ throw Error("MultipleRelationFieldBinding expected a collection whose engine supports relations");
48
59
  }
49
60
  const resolvedProperty = resolveRelationProperty(property, parentCollection, propertyKey)
50
61
  const relation = resolvedProperty;
@@ -66,7 +77,7 @@ export function MultipleRelationFieldBinding({
66
77
  collection,
67
78
  onMultipleEntitiesSelected,
68
79
  selectedEntityIds,
69
- fixedFilter: property.fixedFilter
80
+ fixedFilter: property.admin?.fixedFilter
70
81
  }
71
82
  );
72
83
 
@@ -92,8 +103,8 @@ export function MultipleRelationFieldBinding({
92
103
  onClick={onEntryClick}
93
104
  hover={!disabled}
94
105
  relation={entryValue}
95
- includeId={property.includeId}
96
- includeEntityLink={property.includeEntityLink}
106
+ includeId={property.admin?.includeId}
107
+ includeEntityLink={property.admin?.includeEntityLink}
97
108
  />
98
109
  );
99
110
  }, [relation, property.admin?.previewProperties, value]);
@@ -85,7 +85,7 @@ function ReferenceFieldBindingInternal({
85
85
  collection,
86
86
  onSingleEntitySelected,
87
87
  selectedEntityIds: validValue && refValue ? [refValue.id] : undefined,
88
- fixedFilter: property.fixedFilter
88
+ fixedFilter: property.admin?.fixedFilter
89
89
  }
90
90
  );
91
91
 
@@ -115,8 +115,8 @@ function ReferenceFieldBindingInternal({
115
115
  size={size}
116
116
  onClick={disabled || isSubmitting ? undefined : onEntryClick}
117
117
  reference={refValue}
118
- includeEntityLink={property.includeEntityLink}
119
- includeId={property.includeId}
118
+ includeEntityLink={property.admin?.includeEntityLink}
119
+ includeId={property.admin?.includeId}
120
120
  />}
121
121
 
122
122
  {!refValue && <div className="justify-center text-left">
@@ -22,8 +22,19 @@ export function RelationFieldBinding(props: FieldProps<RelationProperty>) {
22
22
  }
23
23
  const collection = context.collection;
24
24
  const capabilities = collection ? getDataSourceCapabilities(collection.engine) : undefined;
25
- if (!collection || !capabilities?.supportsRelations || !("relations" in collection) || !collection.relations) {
26
- throw Error("RelationFieldBinding expected a collection with relations support");
25
+ // Only two things have to hold here: there is a collection, and its engine
26
+ // has relations at all. Whether *this* property's relation can be resolved
27
+ // is `resolveRelationProperty`'s question, and it answers it across all
28
+ // three forms — a pre-stamped `resolvedRelation`, an inline `relation`, or
29
+ // a lookup by name in the collection's `relations` array — with an error
30
+ // naming the property and the collection when it cannot.
31
+ //
32
+ // This used to demand the `relations` array as well, which the inline form
33
+ // does not produce. That is the documented way to declare a relation, so
34
+ // every collection using it threw here and rendered the error boundary
35
+ // instead of the field.
36
+ if (!collection || !capabilities?.supportsRelations) {
37
+ throw Error("RelationFieldBinding expected a collection whose engine supports relations");
27
38
  }
28
39
  const resolvedProperty = resolveRelationProperty(property, collection, propertyKey);
29
40
  const relation = resolvedProperty;
@@ -82,7 +93,7 @@ function RelationSelectorBinding({
82
93
  }
83
94
  }}
84
95
  disabled={disabled || isSubmitting}
85
- fixedFilter={property.fixedFilter}
96
+ fixedFilter={property.admin?.fixedFilter}
86
97
  size={selectorSize}
87
98
  />
88
99
 
@@ -127,7 +138,7 @@ function SingleRelationFieldBinding({
127
138
  collection,
128
139
  onSingleEntitySelected,
129
140
  selectedEntityIds: validValue && normalizedValue ? [normalizedValue.id] : undefined,
130
- fixedFilter: property.fixedFilter
141
+ fixedFilter: property.admin?.fixedFilter
131
142
  });
132
143
 
133
144
  const onEntryClick = (e: React.SyntheticEvent) => {
@@ -158,8 +169,8 @@ function SingleRelationFieldBinding({
158
169
  size={size}
159
170
  onClick={disabled || isSubmitting ? undefined : onEntryClick}
160
171
  relation={usedRelation}
161
- includeEntityLink={property.includeEntityLink}
162
- includeId={property.includeId}
172
+ includeEntityLink={property.admin?.includeEntityLink}
173
+ includeId={property.admin?.includeId}
163
174
  />}
164
175
 
165
176
  {!value && <div className="justify-center text-left">
@@ -92,8 +92,8 @@ export function RepeatFieldBinding({
92
92
  </ErrorBoundary>;
93
93
  };
94
94
 
95
- const canAddElements = !property.admin?.disabled && !isSubmitting && !disabled && (property.canAddElements || property.canAddElements === undefined);
96
- const sortable = property.sortable === undefined ? true : property.sortable;
95
+ const canAddElements = !property.admin?.disabled && !isSubmitting && !disabled && (property.admin?.canAddElements || property.admin?.canAddElements === undefined);
96
+ const sortable = property.admin?.sortable === undefined ? true : property.admin.sortable;
97
97
  const arrayContainer = <ArrayContainer droppableId={propertyKey}
98
98
  addLabel={property.name ? t("add_to_field", { fieldName: property.name }) : t("add_entry")}
99
99
  value={(value as unknown[]) ?? []}
@@ -13,4 +13,4 @@ export * from "./navigation/useResolvedViews";
13
13
  export * from "./navigation/useTopLevelNavigation";
14
14
  export * from "./useSideDialogsController";
15
15
  export * from "./useBreadcrumbsController";
16
- export * from "./useCMSContext";
16
+ export * from "./useAdminContext";
@@ -91,8 +91,14 @@ function injectAuthCollectionConfig(
91
91
  if (actionToInject) {
92
92
  const injectedAction = actionToInject;
93
93
  const existing = result.entityActions ?? [];
94
- const alreadyHas = existing.some(
95
- (a) => a.key != null && a.key === injectedAction.key
94
+ // An entry is either the action or the key of an app-level one, and a
95
+ // collection that names `"reset_password"` already has this action — so
96
+ // the string form has to count here too, or the reset action is injected
97
+ // a second time and the user gets two identical buttons.
98
+ const alreadyHas = existing.some((a) =>
99
+ typeof a === "string"
100
+ ? a === injectedAction.key
101
+ : a.key != null && a.key === injectedAction.key
96
102
  );
97
103
  if (!alreadyHas) {
98
104
  result = {
@@ -238,13 +244,13 @@ export async function resolveAppViews(
238
244
  if (count > 1) {
239
245
  console.warn(
240
246
  `[Rebase] Duplicate view slug "${slug}" detected (${count} views). ` +
241
- `Last-write-wins. Ensure unique slugs across CMS views and plugins.`
247
+ `Last-write-wins. Ensure unique slugs across admin views and plugins.`
242
248
  );
243
249
  }
244
250
  });
245
251
  }
246
252
 
247
- // Filter by roles — applies to CMS, plugin, and builder-returned views
253
+ // Filter by roles — applies to admin, plugin, and builder-returned views
248
254
  resolvedViews = filterViewsByRole(resolvedViews, authController);
249
255
 
250
256
  return resolvedViews;
@@ -25,13 +25,13 @@ import {
25
25
  } from "./navigation/contexts";
26
26
 
27
27
  /**
28
- * The CMS context extends the core RebaseContext with CMS-specific
29
- * controllers that are only available inside the CMS routing tree.
28
+ * The admin context extends the core RebaseContext with admin-specific
29
+ * controllers that are only available inside the admin routing tree.
30
30
  *
31
- * Use {@link useCMSContext} to obtain an instance.
31
+ * Use {@link useAdminContext} to obtain an instance.
32
32
  * @group Hooks and utilities
33
33
  */
34
- export type CMSContext<
34
+ export type AdminContext<
35
35
  DB = Record<string, unknown>,
36
36
  USER extends User = User,
37
37
  AuthControllerType extends AuthController<USER> = AuthController<USER>
@@ -44,20 +44,20 @@ export type CMSContext<
44
44
  };
45
45
 
46
46
  /**
47
- * Hook that builds a fully-populated CMS context by combining the
48
- * core {@link RebaseContext} with CMS-specific controllers.
47
+ * Hook that builds a fully-populated admin context by combining the
48
+ * core {@link RebaseContext} with admin-specific controllers.
49
49
  *
50
- * Use this instead of `useRebaseContext()` in CMS components that
50
+ * Use this instead of `useRebaseContext()` in admin components that
51
51
  * need to pass context to entity action callbacks, plugin slots,
52
- * or any consumer that expects CMS controllers on the context object.
52
+ * or any consumer that expects admin controllers on the context object.
53
53
  *
54
54
  * @group Hooks and utilities
55
55
  */
56
- export function useCMSContext<
56
+ export function useAdminContext<
57
57
  DB = Record<string, unknown>,
58
58
  USER extends User = User,
59
59
  AuthControllerType extends AuthController<USER> = AuthController<USER>
60
- >(): CMSContext<DB, USER, AuthControllerType> {
60
+ >(): AdminContext<DB, USER, AuthControllerType> {
61
61
  const baseContext = useRebaseContext<USER, AuthControllerType>();
62
62
  const sidePanelController = useSidePanel();
63
63
  const sideDialogsController = useSideDialogsController();
@@ -1,17 +1,13 @@
1
1
  import { useCallback, useEffect, useRef, useState, useMemo } from "react";
2
2
  import { useApiConfig } from "@rebasepro/app";
3
+ import type { EntityHistoryEntry } from "@rebasepro/types";
3
4
 
4
- export interface HistoryEntryData {
5
- id: string;
6
- table_name: string;
7
- entity_id: string;
8
- action: "create" | "update" | "delete";
9
- changed_fields: string[] | null;
10
- values: Record<string, unknown> | null;
11
- previous_values: Record<string, unknown> | null;
12
- updated_by: string | null;
13
- updated_at: string;
14
- }
5
+ /**
6
+ * What the history REST endpoint returns. The fourth declaration of this shape
7
+ * — the two drivers had one each and disagreed on `updated_at` — now an alias
8
+ * of the one in `@rebasepro/types`.
9
+ */
10
+ export type HistoryEntryData = EntityHistoryEntry;
15
11
 
16
12
  export interface UseEntityHistoryResult {
17
13
  entries: HistoryEntryData[];
@@ -4,7 +4,7 @@ import { Entity, User } from "@rebasepro/types";
4
4
  import { RebaseContext } from "@rebasepro/admin-types";
5
5
  import { useEffect, useState, useMemo } from "react";
6
6
  import { getNavigationEntriesFromPath } from "@rebasepro/app";
7
- import { useCMSContext } from "./useCMSContext";
7
+ import { useAdminContext } from "./useAdminContext";
8
8
 
9
9
  /**
10
10
  * @see resolveNavigationFrom
@@ -136,7 +136,7 @@ export function useResolvedNavigationFrom<M extends Record<string, unknown>>(
136
136
  path
137
137
  }: NavigationFromProps): NavigationFrom<M> {
138
138
 
139
- const context = useCMSContext();
139
+ const context = useAdminContext();
140
140
  const { navigationStateController, collectionRegistryController } = context;
141
141
 
142
142
  const [data, setData] = useState<ResolvedNavigationEntry<M>[] | undefined>();
package/src/index.ts CHANGED
@@ -1,4 +1,4 @@
1
- // CMS-specific types take priority over base types when names conflict
1
+ // admin-specific types take priority over base types when names conflict
2
2
  export * from "./types";
3
3
  // Editor types only — the full ProseMirror editor (RichTextEditor) is a heavy import (~300 KB)
4
4
  // and is available as a separate entry point: @rebasepro/admin/editor
@@ -77,7 +77,7 @@ export {
77
77
  mergeEntityActions,
78
78
  resolveEntityAction,
79
79
  resolveEntityView,
80
- // Property utilities (moved from @rebasepro/app — property-aware logic belongs in CMS)
80
+ // Property utilities (moved from @rebasepro/app — property-aware logic belongs in admin)
81
81
  isReferenceProperty,
82
82
  isRelationProperty,
83
83
  getIconForWidget,
@@ -87,7 +87,7 @@ export {
87
87
  getBracketNotation,
88
88
  getPropertiesWithPropertiesOrder,
89
89
  getDefaultPropertiesOrder,
90
- // Preview utilities (moved from @rebasepro/app — property-aware logic belongs in CMS)
90
+ // Preview utilities (moved from @rebasepro/app — property-aware logic belongs in admin)
91
91
  getEntityPreviewKeys,
92
92
  getEntityTitlePropertyKey,
93
93
  getEntityTitlePropertyKeyForEntity
@@ -102,7 +102,7 @@ export { useCollectionDataController, createStaticDataController } from "./util/
102
102
  export * from "./data_import";
103
103
  export * from "./data_export";
104
104
 
105
- // Collection editor — moved from @rebasepro/studio (CMS-dependent visual schema editor)
105
+ // Collection editor — moved from @rebasepro/studio (admin-dependent visual schema editor)
106
106
  export * from "./collection_editor";
107
107
 
108
108
 
@@ -195,8 +195,8 @@ export const PropertyPreview = React.memo(function PropertyPreview<P extends Pro
195
195
  content = <ReferencePreview
196
196
  disabled={!property.path}
197
197
  previewProperties={property.admin?.previewProperties}
198
- includeId={property.includeId}
199
- includeEntityLink={property.includeEntityLink}
198
+ includeId={property.admin?.includeId}
199
+ includeEntityLink={property.admin?.includeEntityLink}
200
200
  size={props.size}
201
201
  reference={value as EntityReference}
202
202
  textOnly={props.textOnly}
@@ -224,8 +224,8 @@ export const PropertyPreview = React.memo(function PropertyPreview<P extends Pro
224
224
  <RelationPreview
225
225
  disabled={!property.relation}
226
226
  previewProperties={property.admin?.previewProperties}
227
- includeId={property.includeId}
228
- includeEntityLink={property.includeEntityLink}
227
+ includeId={property.admin?.includeId}
228
+ includeEntityLink={property.admin?.includeEntityLink}
229
229
  size={"small"}
230
230
  relation={entityRelation}
231
231
  textOnly={props.textOnly}
@@ -242,8 +242,8 @@ export const PropertyPreview = React.memo(function PropertyPreview<P extends Pro
242
242
  content = <RelationPreview
243
243
  disabled={!property.relation}
244
244
  previewProperties={property.admin?.previewProperties}
245
- includeId={property.includeId}
246
- includeEntityLink={property.includeEntityLink}
245
+ includeId={property.admin?.includeId}
246
+ includeEntityLink={property.admin?.includeEntityLink}
247
247
  size={props.size}
248
248
  relation={relationValue}
249
249
  textOnly={props.textOnly}
@@ -158,7 +158,7 @@ function RelationPreviewExisting<M extends Record<string, unknown> = Record<stri
158
158
  collection: AdminCollection<M>
159
159
  }) {
160
160
 
161
- // CMS wire format embeds relation data as { id, path, values }
161
+ // admin wire format embeds relation data as { id, path, values }
162
162
  const passedEntity = relation.data as Entity<M> | undefined;
163
163
  const ResolvedEntityPreview = useComponentOverride("EntityPreview", EntityPreviewBinding);
164
164
  const customizationController = useCustomizationController();
@@ -28,7 +28,7 @@ export function ArrayOfMapsPreview({
28
28
  throw Error(`You need to specify a 'properties' prop (or specify a custom field) in your map property '${propertyKey}'${mapProperty.name ? ` ("${mapProperty.name}")` : ""}`);
29
29
  }
30
30
  const values = value as Record<string, unknown>[];
31
- const previewProperties: string[] | undefined = (mapProperty as MapProperty).previewProperties;
31
+ const previewProperties: string[] | undefined = (mapProperty as MapProperty).admin?.previewProperties;
32
32
 
33
33
  if (!values) return null;
34
34
 
@@ -33,8 +33,8 @@ export function ArrayOfReferencesPreview({
33
33
  previewProperties={ofProperty.admin?.previewProperties}
34
34
  size={childSize}
35
35
  reference={reference}
36
- includeId={ofProperty.includeId}
37
- includeEntityLink={ofProperty.includeEntityLink}
36
+ includeId={ofProperty.admin?.includeId}
37
+ includeEntityLink={ofProperty.admin?.includeEntityLink}
38
38
  />
39
39
  </div>;
40
40
  }
@@ -39,8 +39,8 @@ export function ArrayOfRelationsPreview({
39
39
  previewProperties={ofProperty.admin?.previewProperties}
40
40
  size={"small"}
41
41
  relation={entityRelation}
42
- includeId={ofProperty.includeId}
43
- includeEntityLink={ofProperty.includeEntityLink}
42
+ includeId={ofProperty.admin?.includeId}
43
+ includeEntityLink={ofProperty.admin?.includeEntityLink}
44
44
  />
45
45
  </div>
46
46
  );
@@ -41,7 +41,7 @@ export function SkeletonPropertyComponent({
41
41
  content = <>{arrayProperty.of.map((p, i) => renderGenericArrayCell(p, i))} </>;
42
42
  } else {
43
43
  if (arrayProperty.of.type === "map" && arrayProperty.of.properties) {
44
- content = renderArrayOfMaps(arrayProperty.of.properties, size, arrayProperty.of.previewProperties);
44
+ content = renderArrayOfMaps(arrayProperty.of.properties, size, arrayProperty.of.admin?.previewProperties);
45
45
  } else if (arrayProperty.of.type === "string") {
46
46
  if (arrayProperty.of.enum) {
47
47
  content = renderArrayEnumTableCell();
@@ -79,7 +79,7 @@ function renderMap<T extends Record<string, any>>(property: MapProperty, size: P
79
79
  if (size === "large") {
80
80
  mapPropertyKeys = Object.keys(property.properties);
81
81
  } else {
82
- mapPropertyKeys = (property.previewProperties || Object.keys(property.properties)) as string[];
82
+ mapPropertyKeys = (property.admin?.previewProperties || Object.keys(property.properties)) as string[];
83
83
  if (size === "medium")
84
84
  mapPropertyKeys = mapPropertyKeys.slice(0, 3);
85
85
  else if (size === "small")
@@ -7,7 +7,7 @@ import { EntityFormActionsProps } from "./EntityFormActionsProps";
7
7
 
8
8
  /**
9
9
  * Props for the headless EntityForm component.
10
- * This form can be used without CMS context — all backend concerns
10
+ * This form can be used without admin context — all backend concerns
11
11
  * (save, caching, analytics, plugin slots) are provided via callbacks.
12
12
  */
13
13
  export type EntityFormProps<M extends Record<string, unknown>> = {
@@ -51,7 +51,7 @@ export type EntityFormProps<M extends Record<string, unknown>> = {
51
51
 
52
52
  children?: React.ReactNode;
53
53
 
54
- // --- Headless callbacks (replace internal CMS hooks) ---
54
+ // --- Headless callbacks (replace internal admin hooks) ---
55
55
 
56
56
  /**
57
57
  * Called on form submit with the values to save.
@@ -243,7 +243,7 @@ export interface PropertyFieldBindingProps<M extends Record<string, unknown> = R
243
243
  propertyKey: string;
244
244
 
245
245
  /**
246
- * The CMS property you are binding this field to
246
+ * The admin property you are binding this field to
247
247
  */
248
248
  property: Property;
249
249