@rebasepro/admin 0.3.0 → 0.5.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 (106) hide show
  1. package/README.md +135 -0
  2. package/dist/{CollectionEditorDialog-D0VqpLPO.js → CollectionEditorDialog-C8E440oK.js} +22 -5
  3. package/dist/CollectionEditorDialog-C8E440oK.js.map +1 -0
  4. package/dist/{CollectionsStudioView-Bc3Rxxc2.js → CollectionsStudioView-Brr_b06v.js} +4 -4
  5. package/dist/{CollectionsStudioView-Bc3Rxxc2.js.map → CollectionsStudioView-Brr_b06v.js.map} +1 -1
  6. package/dist/{ExportCollectionAction-Ckc-09BQ.js → ExportCollectionAction-D3TTYXrq.js} +35 -27
  7. package/dist/ExportCollectionAction-D3TTYXrq.js.map +1 -0
  8. package/dist/{ImportCollectionAction-BqjIrC3Z.js → ImportCollectionAction-Dn7lya6E.js} +2 -2
  9. package/dist/{ImportCollectionAction-BqjIrC3Z.js.map → ImportCollectionAction-Dn7lya6E.js.map} +1 -1
  10. package/dist/{PropertyEditView-CvRSV-A2.js → PropertyEditView-ChBleIbN.js} +4 -4
  11. package/dist/PropertyEditView-ChBleIbN.js.map +1 -0
  12. package/dist/collection_editor/ui/collection_editor/PropertyEditView.d.ts +1 -0
  13. package/dist/collection_editor/ui/collection_editor/templates/blog_template.d.ts +1 -1
  14. package/dist/collection_editor/ui/collection_editor/templates/pages_template.d.ts +1 -1
  15. package/dist/collection_editor/ui/collection_editor/templates/products_template.d.ts +1 -1
  16. package/dist/collection_editor/ui/collection_editor/templates/users_template.d.ts +1 -1
  17. package/dist/collection_editor_ui.js +3 -3
  18. package/dist/components/CollectionPanel.d.ts +83 -0
  19. package/dist/components/EntityDetailView.d.ts +0 -3
  20. package/dist/components/EntityEditView.d.ts +2 -3
  21. package/dist/components/RebaseRouteDefs.d.ts +1 -1
  22. package/dist/components/SelectableTable/SelectableTable.d.ts +2 -2
  23. package/dist/components/SelectableTable/SelectableTableContext.d.ts +1 -1
  24. package/dist/components/SelectableTable/SelectionStore.d.ts +3 -3
  25. package/dist/components/admin/index.d.ts +1 -3
  26. package/dist/components/index.d.ts +1 -0
  27. package/dist/editor.js +15 -5
  28. package/dist/editor.js.map +1 -1
  29. package/dist/hooks/navigation/useBuildNavigationStateController.d.ts +1 -1
  30. package/dist/hooks/navigation/useResolvedViews.d.ts +2 -5
  31. package/dist/{index-BCcLwgfe.js → index-D3S9x_RP.js} +2 -2
  32. package/dist/{index-BCcLwgfe.js.map → index-D3S9x_RP.js.map} +1 -1
  33. package/dist/{index-DY2k5TtG.js → index-D9RNzm01.js} +3 -3
  34. package/dist/index-D9RNzm01.js.map +1 -0
  35. package/dist/{index-UQOMHwt1.js → index-DrrT332R.js} +3 -3
  36. package/dist/index-DrrT332R.js.map +1 -0
  37. package/dist/index.d.ts +2 -4
  38. package/dist/index.js +228 -295
  39. package/dist/index.js.map +1 -1
  40. package/dist/util/entity_view_constants.d.ts +6 -0
  41. package/dist/util/resolutions.d.ts +1 -2
  42. package/dist/{util-ZM9gQuCv.js → util-COYYD8zr.js} +473 -880
  43. package/dist/util-COYYD8zr.js.map +1 -0
  44. package/package.json +9 -10
  45. package/src/collection_editor/pgColumnToProperty.ts +19 -2
  46. package/src/collection_editor/ui/collection_editor/CollectionRelationsTab.tsx +2 -2
  47. package/src/collection_editor/ui/collection_editor/PropertyEditView.tsx +2 -1
  48. package/src/collection_editor/ui/collection_editor/PropertyFieldPreview.tsx +2 -2
  49. package/src/collection_editor/ui/collection_editor/properties/conditions/ConditionsEditor.tsx +2 -1
  50. package/src/collection_editor/ui/collection_editor/properties/conditions/EnumConditionsEditor.tsx +1 -1
  51. package/src/collection_editor/ui/collection_editor/templates/blog_template.ts +1 -1
  52. package/src/collection_editor/ui/collection_editor/templates/pages_template.ts +1 -1
  53. package/src/collection_editor/ui/collection_editor/templates/products_template.ts +1 -1
  54. package/src/collection_editor/ui/collection_editor/templates/users_template.ts +1 -1
  55. package/src/collection_editor/validateCollectionJson.ts +12 -12
  56. package/src/components/CollectionPanel.tsx +155 -0
  57. package/src/components/DefaultDrawer.tsx +2 -2
  58. package/src/components/EntityCollectionTable/internal/popup_field/useDraggable.tsx +5 -2
  59. package/src/components/EntityCollectionView/EntityCollectionCardView.tsx +64 -34
  60. package/src/components/EntityCollectionView/EntityCollectionListView.tsx +8 -1
  61. package/src/components/EntityCollectionView/EntityCollectionView.tsx +4 -1
  62. package/src/components/EntityCollectionView/hooks/useCollectionInlineEditor.ts +1 -0
  63. package/src/components/EntityCollectionView/useEntityPreviewSlots.ts +68 -18
  64. package/src/components/EntityDetailView.tsx +32 -36
  65. package/src/components/EntityEditView.tsx +101 -50
  66. package/src/components/EntityEditViewFormActions.tsx +4 -4
  67. package/src/components/EntitySidePanel.tsx +50 -14
  68. package/src/components/PropertyCollectionView.tsx +1 -0
  69. package/src/components/RebaseRouteDefs.tsx +4 -6
  70. package/src/components/SelectableTable/SelectableTable.tsx +24 -22
  71. package/src/components/SelectableTable/SelectableTableContext.tsx +2 -1
  72. package/src/components/SelectableTable/SelectionStore.ts +4 -4
  73. package/src/components/admin/index.ts +1 -3
  74. package/src/components/index.ts +3 -3
  75. package/src/data_export/export/BasicExportAction.tsx +11 -9
  76. package/src/data_export/export/ExportCollectionAction.tsx +15 -13
  77. package/src/editor/editor.tsx +20 -4
  78. package/src/form/EntityForm.tsx +3 -3
  79. package/src/form/PropertyFieldBinding.tsx +1 -1
  80. package/src/form/field_bindings/KeyValueFieldBinding.tsx +1 -1
  81. package/src/form/field_bindings/MapFieldBinding.tsx +3 -45
  82. package/src/form/field_bindings/ReadOnlyFieldBinding.tsx +3 -1
  83. package/src/hooks/navigation/useBuildNavigationStateController.tsx +2 -3
  84. package/src/hooks/navigation/useResolvedCollections.ts +2 -10
  85. package/src/hooks/navigation/useResolvedViews.tsx +6 -48
  86. package/src/hooks/useBuildSideEntityController.tsx +20 -3
  87. package/src/index.ts +6 -5
  88. package/src/util/entity_view_constants.ts +6 -0
  89. package/src/util/previews.ts +9 -1
  90. package/src/util/resolutions.ts +2 -6
  91. package/dist/CollectionEditorDialog-D0VqpLPO.js.map +0 -1
  92. package/dist/ExportCollectionAction-Ckc-09BQ.js.map +0 -1
  93. package/dist/PropertyEditView-CvRSV-A2.js.map +0 -1
  94. package/dist/components/admin/RoleChip.d.ts +0 -4
  95. package/dist/components/admin/RolesFilterSelect.d.ts +0 -2
  96. package/dist/components/admin/RolesView.d.ts +0 -4
  97. package/dist/components/admin/UserRolesSelectField.d.ts +0 -2
  98. package/dist/components/admin/UsersView.d.ts +0 -4
  99. package/dist/index-DY2k5TtG.js.map +0 -1
  100. package/dist/index-UQOMHwt1.js.map +0 -1
  101. package/dist/util-ZM9gQuCv.js.map +0 -1
  102. package/src/components/admin/RoleChip.tsx +0 -23
  103. package/src/components/admin/RolesFilterSelect.tsx +0 -45
  104. package/src/components/admin/RolesView.tsx +0 -470
  105. package/src/components/admin/UserRolesSelectField.tsx +0 -50
  106. package/src/components/admin/UsersView.tsx +0 -693
@@ -1,8 +1,8 @@
1
1
  import { createSelectionStore } from "./SelectionStore";
2
2
  import type { Property } from "@rebasepro/types";
3
3
  import React, { useCallback, useEffect, useMemo, useRef } from "react";
4
- import { CollectionSize, Entity, EntityTableController, FilterValues, SelectedCellProps } from "@rebasepro/types";
5
- import { CellRendererParams, VirtualTable, VirtualTableColumn } from "@rebasepro/ui";
4
+ import { CollectionSize, Entity, EntityRelation, EntityTableController, FilterValues, SelectedCellProps } from "@rebasepro/types";
5
+ import { CellRendererParams, VirtualTable, VirtualTableColumn, VirtualTableFilterValues, OnRowClickParams, VirtualTableWhereFilterOp } from "@rebasepro/ui";
6
6
  import { enumToObjectEntries } from "@rebasepro/common";
7
7
  import { DEFAULT_PAGE_SIZE, EntityCollectionTableController, OnCellValueChange, OnColumnResizeParams } from "@rebasepro/core";
8
8
  import { FilterFormFieldProps } from "@rebasepro/ui";
@@ -20,7 +20,7 @@ export type SelectableTableProps<M extends Record<string, unknown>> = {
20
20
  /**
21
21
  * Callback when a cell value changes.
22
22
  */
23
- onValueChange?: OnCellValueChange<any, M>;
23
+ onValueChange?: OnCellValueChange<unknown, M>;
24
24
 
25
25
  columns: VirtualTableColumn[];
26
26
 
@@ -100,7 +100,7 @@ export type SelectableTableProps<M extends Record<string, unknown>> = {
100
100
  * bust the cell memo comparator, triggering re-render of cells
101
101
  * even when rowData hasn't changed.
102
102
  */
103
- extraData?: Record<string, any>;
103
+ extraData?: unknown;
104
104
  }
105
105
 
106
106
  /**
@@ -184,12 +184,10 @@ export const SelectableTable = function SelectableTable<M extends Record<string,
184
184
  setItemCount?.(pageSize);
185
185
  }, [pageSize]);
186
186
 
187
- const onRowClick = useCallback(({ rowData }: {
188
- rowData: Entity<M>
189
- }) => {
187
+ const onRowClick = useCallback(({ rowData }: OnRowClickParams<Record<string, unknown>>) => {
190
188
  if (inlineEditing)
191
189
  return;
192
- return onEntityClick && onEntityClick(rowData);
190
+ return onEntityClick && onEntityClick(rowData as unknown as Entity<M>);
193
191
  }, [onEntityClick, inlineEditing]);
194
192
 
195
193
  const select = useCallback((cell?: SelectedCellProps<M>) => {
@@ -223,18 +221,22 @@ export const SelectableTable = function SelectableTable<M extends Record<string,
223
221
  };
224
222
  }, [unselect]);
225
223
 
226
- const onFilterUpdate = useCallback((updatedFilterValues?: FilterValues<any>) => {
227
- setFilterValues?.({ ...updatedFilterValues,
228
- ...fixedFilter } as FilterValues<any>);
229
- }, [fixedFilter]);
224
+ const onFilterUpdate = useCallback((updatedFilterValues?: VirtualTableFilterValues<string>) => {
225
+ if (setFilterValues) {
226
+ setFilterValues({
227
+ ...updatedFilterValues,
228
+ ...fixedFilter
229
+ } as FilterValues<Extract<keyof M, string> | (string & {})>);
230
+ }
231
+ }, [fixedFilter, setFilterValues]);
230
232
 
231
233
  const contextValue = useMemo(() => ({
232
- setPopupCell: setPopupCell as ((cell?: SelectedCellProps<M>) => void),
233
234
  select,
234
235
  onValueChange,
235
236
  size: size ?? "m",
236
- selectionStore
237
- } as EntityCollectionTableController<any>), [setPopupCell, select, onValueChange, size, selectionStore]);
237
+ selectionStore,
238
+ setPopupCell
239
+ } as unknown as EntityCollectionTableController<Record<string, unknown>>), [setPopupCell, select, onValueChange, size, selectionStore]);
238
240
 
239
241
  return (
240
242
  <SelectableTableContext.Provider
@@ -243,11 +245,11 @@ export const SelectableTable = function SelectableTable<M extends Record<string,
243
245
  ref={ref}>
244
246
 
245
247
  <VirtualTable
246
- data={data as any}
248
+ data={data as unknown as Record<string, unknown>[]}
247
249
  columns={columns}
248
250
  // @ts-ignore
249
251
  cellRenderer={cellRenderer}
250
- onRowClick={inlineEditing ? undefined : (onEntityClick ? (onRowClick as any) : undefined)}
252
+ onRowClick={inlineEditing ? undefined : (onEntityClick ? onRowClick : undefined)}
251
253
  onEndReached={loadNextPage}
252
254
  onResetPagination={resetPagination}
253
255
  error={dataLoadingError}
@@ -262,9 +264,9 @@ export const SelectableTable = function SelectableTable<M extends Record<string,
262
264
  initialScroll={initialScroll}
263
265
  onScroll={onScroll}
264
266
  checkFilterCombination={checkFilterCombination}
265
- createFilterField={filterable ? (createFilterField as any) : undefined}
266
- rowClassName={useCallback((entity: any) => {
267
- return highlightedRow?.(entity) ? "bg-surface-accent-50 dark:!bg-surface-accent-900" : "";
267
+ createFilterField={filterable ? createFilterField : undefined}
268
+ rowClassName={useCallback((entity: Record<string, unknown>) => {
269
+ return highlightedRow?.(entity as unknown as Entity<M>) ? "bg-surface-accent-50 dark:!bg-surface-accent-900" : "";
268
270
  }, [highlightedRow])}
269
271
  className="grow"
270
272
  emptyComponent={emptyComponent}
@@ -287,7 +289,7 @@ function createFilterField({
287
289
  column,
288
290
  hidden,
289
291
  setHidden
290
- }: FilterFormFieldProps<any>): React.ReactNode {
292
+ }: FilterFormFieldProps<unknown>): React.ReactNode {
291
293
 
292
294
  if (!column.custom) {
293
295
  return null;
@@ -313,7 +315,7 @@ function createFilterField({
313
315
  hidden={hidden}
314
316
  setHidden={setHidden}/>;
315
317
  } else if (baseProperty.type === "relation" && baseProperty.relation) {
316
- return <RelationFilterField value={filterValue as any}
318
+ return <RelationFilterField value={filterValue as [VirtualTableWhereFilterOp, EntityRelation | EntityRelation[] | null]}
317
319
  setValue={setFilterValue}
318
320
  name={id as string}
319
321
  relation={baseProperty.relation}
@@ -3,4 +3,5 @@ import { EntityCollectionTableController } from "@rebasepro/core";
3
3
 
4
4
  export const SelectableTableContext = React.createContext<EntityCollectionTableController<Record<string, unknown>>>(null! as EntityCollectionTableController<Record<string, unknown>>);
5
5
 
6
- export const useSelectableTableController = () => useContext<EntityCollectionTableController<any>>(SelectableTableContext);
6
+ export const useSelectableTableController = <M extends Record<string, unknown> = Record<string, unknown>>() =>
7
+ useContext(SelectableTableContext) as unknown as EntityCollectionTableController<M>;
@@ -15,10 +15,10 @@ import { SelectedCellProps } from "@rebasepro/types";
15
15
  * derivation actually changed will re-render.
16
16
  */
17
17
  export function createSelectionStore() {
18
- let selectedCell: SelectedCellProps<any> | undefined = undefined;
18
+ let selectedCell: SelectedCellProps | undefined = undefined;
19
19
  const listeners = new Set<() => void>();
20
20
 
21
- function getSnapshot(): SelectedCellProps<any> | undefined {
21
+ function getSnapshot(): SelectedCellProps | undefined {
22
22
  return selectedCell;
23
23
  }
24
24
 
@@ -27,7 +27,7 @@ export function createSelectionStore() {
27
27
  return () => listeners.delete(listener);
28
28
  }
29
29
 
30
- function select(cell: SelectedCellProps<any> | undefined) {
30
+ function select(cell: SelectedCellProps | undefined) {
31
31
  selectedCell = cell;
32
32
  listeners.forEach(l => l());
33
33
  }
@@ -43,7 +43,7 @@ export type SelectionStore = ReturnType<typeof createSelectionStore>;
43
43
  * `selected` boolean actually changes (not on every store update).
44
44
  */
45
45
  export function useCellSelected(
46
- store: { getSnapshot: () => SelectedCellProps<any> | undefined; subscribe: (listener: () => void) => () => void },
46
+ store: { getSnapshot: () => SelectedCellProps | undefined; subscribe: (listener: () => void) => () => void },
47
47
  propertyKey: string,
48
48
  entityPath: string,
49
49
  entityId: string | number
@@ -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";
@@ -55,3 +53,5 @@ export * from "./RebaseAuthGate";
55
53
  export * from "./RebaseNavigation";
56
54
  export * from "./RebaseLayout";
57
55
  export * from "./RebaseRouteDefs";
56
+
57
+ export * from "./CollectionPanel";
@@ -1,6 +1,7 @@
1
1
 
2
2
  import React, { useCallback } from "react";
3
3
 
4
+ import { useTranslation } from "@rebasepro/core";
4
5
  import { Entity, Properties } from "@rebasepro/types";
5
6
  import {
6
7
  BooleanSwitchWithLabel,
@@ -31,6 +32,7 @@ export function BasicExportAction({
31
32
  propertiesOrder
32
33
  }: BasicExportActionProps) {
33
34
 
35
+ const { t } = useTranslation();
34
36
  const dateRef = React.useRef<Date>(new Date());
35
37
  const [flattenArrays, setFlattenArrays] = React.useState<boolean>(true);
36
38
  const [exportType, setExportType] = React.useState<"csv" | "json">("csv");
@@ -78,22 +80,22 @@ export function BasicExportAction({
78
80
  onOpenChange={setOpen}
79
81
  maxWidth={"xl"}>
80
82
 
81
- <DialogTitle variant={"h6"}>Export data</DialogTitle>
83
+ <DialogTitle variant={"h6"}>{t("export_data")}</DialogTitle>
82
84
 
83
85
  <DialogContent className={"flex flex-col gap-4 my-4"}>
84
86
 
85
- <div>DownloadIcon the the content of this table as a CSV</div>
87
+ <div>{t("download_table_csv")}</div>
86
88
 
87
89
  <div className={"flex flex-row gap-4"}>
88
90
  <div className={"p-4 flex flex-col"}>
89
91
  <RadioGroup value={exportType} onValueChange={(v) => setExportType(v as "csv" | "json")}>
90
92
  <div className="flex items-center gap-2">
91
93
  <RadioGroupItem value="csv" id="radio-csv"/>
92
- <Label htmlFor="radio-csv">CSV</Label>
94
+ <Label htmlFor="radio-csv">{t("csv")}</Label>
93
95
  </div>
94
96
  <div className="flex items-center gap-2">
95
97
  <RadioGroupItem value="json" id="radio-json"/>
96
- <Label htmlFor="radio-json">JSON</Label>
98
+ <Label htmlFor="radio-json">{t("json")}</Label>
97
99
  </div>
98
100
  </RadioGroup>
99
101
  </div>
@@ -102,11 +104,11 @@ export function BasicExportAction({
102
104
  <RadioGroup value={dateExportType} onValueChange={(v) => setDateExportType(v as "timestamp" | "string")}>
103
105
  <div className="flex items-center gap-2">
104
106
  <RadioGroupItem value="timestamp" id="radio-timestamp"/>
105
- <Label htmlFor="radio-timestamp">Dates as timestamps ({dateRef.current.getTime()})</Label>
107
+ <Label htmlFor="radio-timestamp">{t("dates_as_timestamps")} ({dateRef.current.getTime()})</Label>
106
108
  </div>
107
109
  <div className="flex items-center gap-2">
108
110
  <RadioGroupItem value="string" id="radio-string"/>
109
- <Label htmlFor="radio-string">Dates as strings ({dateRef.current.toISOString()})</Label>
111
+ <Label htmlFor="radio-string">{t("dates_as_strings")} ({dateRef.current.toISOString()})</Label>
110
112
  </div>
111
113
  </RadioGroup>
112
114
  </div>
@@ -117,7 +119,7 @@ export function BasicExportAction({
117
119
  disabled={exportType !== "csv"}
118
120
  value={flattenArrays}
119
121
  onValueChange={setFlattenArrays}
120
- label={"Flatten arrays"}/>
122
+ label={t("flatten_arrays")}/>
121
123
 
122
124
  </DialogContent>
123
125
 
@@ -125,11 +127,11 @@ export function BasicExportAction({
125
127
 
126
128
  <Button onClick={handleClose}
127
129
  variant={"text"}>
128
- Cancel
130
+ {t("cancel")}
129
131
  </Button>
130
132
 
131
133
  <Button onClick={onOkClicked}>
132
- DownloadIcon
134
+ {t("download")}
133
135
  </Button>
134
136
 
135
137
  </DialogActions>
@@ -4,7 +4,8 @@ import React, { useCallback } from "react";
4
4
  import {
5
5
  useAuthController,
6
6
  useCustomizationController,
7
- useData
7
+ useData,
8
+ useTranslation
8
9
  } from "@rebasepro/core";
9
10
  import { useCMSContext } from "../../hooks";
10
11
  import { CollectionActionsProps, Entity, EntityCollection, ExportConfig, RebaseContext, User } from "@rebasepro/types";
@@ -43,6 +44,8 @@ export function ExportCollectionAction<M extends Record<string, unknown>, USER e
43
44
  onAnalyticsEvent?: (event: string, params?: any) => void;
44
45
  }) {
45
46
 
47
+ const { t } = useTranslation();
48
+
46
49
  const exportConfig = typeof collection.exportable === "object" ? collection.exportable : undefined;
47
50
 
48
51
  const dateRef = React.useRef<Date>(new Date());
@@ -184,17 +187,16 @@ export function ExportCollectionAction<M extends Record<string, unknown>, USER e
184
187
  onOpenChange={setOpen}
185
188
  maxWidth={"xl"}>
186
189
 
187
- <DialogTitle variant={"h6"}>Export data</DialogTitle>
190
+ <DialogTitle variant={"h6"}>{t("export_data")}</DialogTitle>
188
191
 
189
192
  <DialogContent className={"flex flex-col gap-4 my-4"}>
190
193
 
191
- <div>DownloadIcon the the content of this table as a CSV</div>
194
+ <div>{t("download_table_csv")}</div>
192
195
 
193
196
  {collectionEntitiesCount !== undefined && collectionEntitiesCount > DOCS_LIMIT &&
194
197
  <Alert color={"warning"}>
195
198
  <div>
196
- This collections has a large number
197
- of documents ({collectionEntitiesCount}).
199
+ {t("large_number_of_documents", { count: collectionEntitiesCount.toString() })}
198
200
  </div>
199
201
  </Alert>}
200
202
 
@@ -203,11 +205,11 @@ export function ExportCollectionAction<M extends Record<string, unknown>, USER e
203
205
  <RadioGroup value={exportType} onValueChange={(v) => setExportType(v as "csv" | "json")}>
204
206
  <div className="flex items-center gap-2">
205
207
  <RadioGroupItem value="csv" id="radio-csv"/>
206
- <Label htmlFor="radio-csv">CSV</Label>
208
+ <Label htmlFor="radio-csv">{t("csv")}</Label>
207
209
  </div>
208
210
  <div className="flex items-center gap-2">
209
211
  <RadioGroupItem value="json" id="radio-json"/>
210
- <Label htmlFor="radio-json">JSON</Label>
212
+ <Label htmlFor="radio-json">{t("json")}</Label>
211
213
  </div>
212
214
  </RadioGroup>
213
215
  </div>
@@ -216,11 +218,11 @@ export function ExportCollectionAction<M extends Record<string, unknown>, USER e
216
218
  <RadioGroup value={dateExportType} onValueChange={(v) => setDateExportType(v as "timestamp" | "string")}>
217
219
  <div className="flex items-center gap-2">
218
220
  <RadioGroupItem value="timestamp" id="radio-timestamp"/>
219
- <Label htmlFor="radio-timestamp">Dates as timestamps ({dateRef.current.getTime()})</Label>
221
+ <Label htmlFor="radio-timestamp">{t("dates_as_timestamps")} ({dateRef.current.getTime()})</Label>
220
222
  </div>
221
223
  <div className="flex items-center gap-2">
222
224
  <RadioGroupItem value="string" id="radio-string"/>
223
- <Label htmlFor="radio-string">Dates as strings ({dateRef.current.toISOString()})</Label>
225
+ <Label htmlFor="radio-string">{t("dates_as_strings")} ({dateRef.current.toISOString()})</Label>
224
226
  </div>
225
227
  </RadioGroup>
226
228
  </div>
@@ -231,13 +233,13 @@ export function ExportCollectionAction<M extends Record<string, unknown>, USER e
231
233
  disabled={exportType !== "csv"}
232
234
  value={flattenArrays}
233
235
  onValueChange={setFlattenArrays}
234
- label={"Flatten arrays"}/>
236
+ label={t("flatten_arrays")}/>
235
237
 
236
238
  <BooleanSwitchWithLabel
237
239
  size={"small"}
238
240
  value={includeUndefinedValues}
239
241
  onValueChange={setIncludeUndefinedValues}
240
- label={"Include undefined values"}/>
242
+ label={t("include_undefined_values")}/>
241
243
 
242
244
  {!canExport && notAllowedView}
243
245
 
@@ -249,12 +251,12 @@ export function ExportCollectionAction<M extends Record<string, unknown>, USER e
249
251
 
250
252
  <Button onClick={handleClose}
251
253
  variant={"text"}>
252
- Cancel
254
+ {t("cancel")}
253
255
  </Button>
254
256
 
255
257
  <Button onClick={onOkClicked}
256
258
  disabled={dataLoading || !canExport}>
257
- DownloadIcon
259
+ {t("download")}
258
260
  </Button>
259
261
 
260
262
  </DialogActions>
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import React, { useEffect, useState, useRef, useMemo } from "react";
3
- import { EditorState } from "prosemirror-state";
3
+ import { EditorState, Selection } from "prosemirror-state";
4
4
  import { cls, defaultBorderMixin, Separator, useInjectStyles, TextareaAutosize } from "@rebasepro/ui";
5
5
  import { useTranslation } from "@rebasepro/core";
6
6
  import { EditorBubble, ImageBubble, SlashCommandMenu, TableBubble, type JSONContent } from "./components";
@@ -236,7 +236,7 @@ onJsonContentChange };
236
236
 
237
237
 
238
238
  return (
239
- <div className="relative min-h-[200px] w-full">
239
+ <div className="relative min-h-[200px] w-full flex flex-col">
240
240
  <button
241
241
  type="button"
242
242
  onClick={handleToggleMarkdown}
@@ -249,10 +249,23 @@ onJsonContentChange };
249
249
  <ProseMirrorContext.Provider value={useMemo(() => ({ state,
250
250
  view }), [state, view])}>
251
251
 
252
- <div style={{ display: isMarkdownMode ? "none" : "block" }}>
252
+ <div style={{ display: isMarkdownMode ? "none" : "flex" }} className="flex-1 flex flex-col">
253
253
  <div
254
254
  ref={editorRef}
255
- className={cls("relative prose dark:prose-invert", proseClass, "prose-headings:font-title prose-headings:font-normal prose-strong:font-semibold prose-code:font-normal prose-blockquote:font-normal prose-a:font-normal font-default focus:outline-none max-w-full px-12 py-4")}
255
+ onClick={(e) => {
256
+ // If the click landed on the wrapper padding (not on the ProseMirror
257
+ // content), focus the editor and place the cursor at the nearest position.
258
+ if (view && e.target === e.currentTarget) {
259
+ view.focus();
260
+ // Place cursor at end of the document
261
+ const end = view.state.doc.content.size;
262
+ const tr = view.state.tr.setSelection(
263
+ Selection.near(view.state.doc.resolve(end), -1)
264
+ );
265
+ view.dispatch(tr);
266
+ }
267
+ }}
268
+ className={cls("relative prose dark:prose-invert cursor-text flex-1", proseClass, "prose-headings:font-title prose-headings:font-normal prose-strong:font-semibold prose-code:font-normal prose-blockquote:font-normal prose-a:font-normal font-default focus:outline-none max-w-full py-4")}
256
269
  />
257
270
 
258
271
  {view && (
@@ -311,6 +324,9 @@ view }), [state, view])}>
311
324
  const cssStyles = `
312
325
  .ProseMirror {
313
326
  box-shadow: none !important;
327
+ padding-left: 2rem;
328
+ padding-right: 2rem;
329
+ min-height: 100%;
314
330
  }
315
331
  .ProseMirror .is-editor-empty:first-child::before {
316
332
  content: attr(data-placeholder);
@@ -311,7 +311,7 @@ export function EntityForm<M extends Record<string, unknown>>({
311
311
  }
312
312
  }
313
313
  },
314
- validation: async (values) => {
314
+ validation: async (values): Promise<Record<string, string>> => {
315
315
  if (!validationSchema) return {};
316
316
  const result = await validationSchema.safeParseAsync(values);
317
317
  if (result.success) return {};
@@ -737,14 +737,14 @@ export function EntityForm<M extends Record<string, unknown>>({
737
737
  entity={entity}
738
738
  layout={forceActionsAtTheBottom ? "bottom" : "responsive"}
739
739
  savingError={savingError}
740
- formex={formex as any}
740
+ formex={formex as FormexController<Record<string, unknown>>}
741
741
  disabled={actionsDisabled}
742
742
  status={status}
743
743
  pluginActions={pluginFormActions ?? []}
744
744
  openEntityMode={openEntityMode}
745
745
  showDefaultActions={showDefaultActions}
746
746
  navigateBack={navigateBack}
747
- formContext={formContext as any}
747
+ formContext={formContext as FormContext<Record<string, unknown>>}
748
748
  />;
749
749
 
750
750
  return (
@@ -219,7 +219,7 @@ function FieldInternal<CustomProps, M extends Record<string, unknown>>
219
219
  collection: context.collection,
220
220
  propertyKey: propertyKey,
221
221
  property: property,
222
- Component: Component as any, // Cast component here since it is a generic component type
222
+ Component: Component as ComponentType<FieldProps<Property, unknown, M>>,
223
223
  plugins: plugins
224
224
  });
225
225
  const UsedComponent: ComponentType<FieldProps<Property, any, any>> = WrappedComponent ?? Component;
@@ -348,7 +348,7 @@ function MapKeyValueRow<T extends Record<string, unknown>>({
348
348
  value={arrayValue[index]}
349
349
  disabled={disabled || !fieldKey}
350
350
  setValue={(newValue) => {
351
- const newArrayValue = [...arrayValue] as any[];
351
+ const newArrayValue = [...arrayValue] as unknown[];
352
352
  newArrayValue[index] = newValue;
353
353
  setValue({
354
354
  ...value,
@@ -1,4 +1,3 @@
1
- import type { Properties } from "@rebasepro/types";
2
1
  import type { FieldProps, PropertyFieldBindingProps } from "../../types/fields";
3
2
  import type { MapProperty } from "@rebasepro/types";
4
3
  import React from "react";
@@ -10,8 +9,7 @@ import { getIconForProperty } from "../../util/property_utils";
10
9
  import { isHidden, isReadOnly } from "@rebasepro/common";
11
10
  import { FieldHelperText, LabelWithIconAndTooltip } from "../components";
12
11
  import { PropertyFieldBinding } from "../PropertyFieldBinding";
13
- import { cls, ExpandablePanel, InputLabel, Select, SelectItem } from "@rebasepro/ui";
14
- import { pick } from "@rebasepro/utils";
12
+ import { cls, ExpandablePanel } from "@rebasepro/ui";
15
13
 
16
14
  /**
17
15
  * Field that renders the children property fields
@@ -35,7 +33,6 @@ export function MapFieldBinding({
35
33
  onPropertyChange
36
34
  }: FieldProps<MapProperty>) {
37
35
 
38
- const pickOnlySomeKeys = property.pickOnlySomeKeys || false;
39
36
  const expanded = property.ui?.expanded === undefined ? true : property.ui?.expanded;
40
37
  const minimalistView = minimalistViewProp || property.ui?.minimalistView;
41
38
  const { t } = useTranslation();
@@ -44,19 +41,7 @@ export function MapFieldBinding({
44
41
  throw Error(`You need to specify a 'properties' prop (or specify a custom field) in your map property ${propertyKey}`);
45
42
  }
46
43
 
47
- let mapProperties: Properties;
48
- if (pickOnlySomeKeys) {
49
- if (value) {
50
- mapProperties = pick(property.properties,
51
- ...Object.keys(value)
52
- .filter(key => key in property.properties!)
53
- ) as Properties;
54
- } else {
55
- mapProperties = {};
56
- }
57
- } else {
58
- mapProperties = property.properties;
59
- }
44
+ const mapProperties = property.properties;
60
45
 
61
46
  const mapFormView = <>
62
47
  <div
@@ -96,7 +81,7 @@ export function MapFieldBinding({
96
81
  }
97
82
  </div>
98
83
 
99
- {/*{pickOnlySomeKeys && buildPickKeysSelect(disabled, property.properties, setValue, value)}*/}
84
+
100
85
 
101
86
  </>
102
87
  ;
@@ -133,30 +118,3 @@ export function MapFieldBinding({
133
118
  );
134
119
  }
135
120
 
136
- const buildPickKeysSelect = (disabled: boolean, properties: Properties, setValue: (value: Record<string, unknown> | null) => void, value: Record<string, unknown> | undefined) => {
137
-
138
- const keys = Object.keys(properties)
139
- .filter((key) => !value || !(key in value));
140
-
141
- const handleAddProperty = (updatedValue: string | string[]) => {
142
- setValue({
143
- ...value,
144
- [updatedValue as string]: null
145
- });
146
- };
147
-
148
- if (!keys.length) return <></>;
149
-
150
- return <div className={"m-4"}>
151
- <InputLabel>Add property</InputLabel>
152
- <Select
153
- value={""}
154
- fullWidth={true}
155
- disabled={disabled}
156
- onValueChange={handleAddProperty}
157
- renderValue={(key) => (properties as Properties)[key].name || key}>
158
- {keys.map((key) => <SelectItem key={key}
159
- value={key}>{(properties as Properties)[key].name || key}</SelectItem>)}
160
- </Select>
161
- </div>;
162
- };
@@ -32,6 +32,8 @@ export function ReadOnlyFieldBinding({
32
32
  // if (!context.entityId)
33
33
  // throw new Error("ReadOnlyFieldBinding: Entity id is null");
34
34
 
35
+ const skipCardWrapper = property.type === "relation" || property.type === "reference";
36
+
35
37
  return (
36
38
 
37
39
  <>
@@ -45,7 +47,7 @@ export function ReadOnlyFieldBinding({
45
47
  }
46
48
 
47
49
  <div
48
- className={cls(paperMixin, "w-full min-h-14 p-4 md:p-6 overflow-x-scroll no-scrollbar")}>
50
+ className={cls(!skipCardWrapper && paperMixin, "w-full min-h-14 overflow-x-scroll no-scrollbar", !skipCardWrapper && "p-4 md:p-6")}>
49
51
 
50
52
  <ErrorBoundary>
51
53
  <PropertyPreview propertyKey={propertyKey}
@@ -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)
@@ -3,7 +3,7 @@ import { useCallback, useEffect, useRef, useState, useMemo } from "react";
3
3
 
4
4
  import { AuthController, CollectionRegistryController, RebaseData, User } from "@rebasepro/types";
5
5
  import type { EntityCollectionsBuilder } from "@rebasepro/types";
6
- import { CollectionRegistry, defaultUsersCollection } from "@rebasepro/common";
6
+ import { CollectionRegistry } from "@rebasepro/common";
7
7
  import { UserManagementDelegate } from "@rebasepro/types";
8
8
 
9
9
  import { resolveCollections } from "./useNavigationResolution";
@@ -95,15 +95,7 @@ export function useResolvedCollections<EC extends EntityCollection, USER extends
95
95
 
96
96
  if (cancelled) return;
97
97
 
98
- // Prepend system defaults; developer collections override via generic dedup.
99
- // Map keyed by slug — last-write-wins, so developer collections overwrite defaults.
100
- const defaults: EntityCollection[] = [];
101
- if (userManagementRef.current) {
102
- defaults.push(defaultUsersCollection);
103
- }
104
- const deduped = Array.from(
105
- new Map([...defaults, ...resolved].map(c => [c.slug, c])).values()
106
- );
98
+ const deduped = [...resolved];
107
99
 
108
100
  // Register with the CollectionRegistry; returns true if changed
109
101
  const changed = collectionRegistryController.collectionRegistryRef.current.registerMultiple(deduped);