@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
@@ -1,291 +0,0 @@
1
- import { Property, StringProperty, NumberProperty, ArrayProperty, TableColumnInfo, TableMetadata, PostgresProperties } from "@rebasepro/types";
2
- import { prettifyIdentifier } from "@rebasepro/utils";
3
- import type { AdminCollection } from "@rebasepro/admin-types";
4
-
5
- /**
6
- * Maps a PostgreSQL column data type to a Rebase property type.
7
- */
8
- function pgTypeToRebaseProperty(column: TableColumnInfo): Property | null {
9
- const {
10
- column_name,
11
- data_type,
12
- udt_name,
13
- is_nullable,
14
- column_default,
15
- enum_values
16
- } = column;
17
-
18
- const required = is_nullable === "NO";
19
- const prettifiedName = prettifyIdentifier(column_name);
20
-
21
- // Detect if this column is a primary key (auto-generated id)
22
- const isAutoId = column_default != null && (
23
- column_default.includes("nextval") ||
24
- column_default.includes("gen_random_uuid") ||
25
- column_default.includes("uuid_generate") ||
26
- column_default.includes("identity")
27
- );
28
-
29
- // USER-DEFINED = PostgreSQL enums
30
- if (data_type === "USER-DEFINED" && enum_values && enum_values.length > 0) {
31
- return {
32
- type: "string",
33
- name: prettifiedName,
34
- enum: enum_values.map((v: string) => ({ id: v,
35
- label: prettifyIdentifier(v) })),
36
- validation: required ? { required: true } : undefined
37
- } as StringProperty;
38
- }
39
-
40
- const dt = data_type.toLowerCase();
41
- switch (dt) {
42
- case "character varying":
43
- case "varchar":
44
- case "text":
45
- case "char":
46
- case "character":
47
- case "citext": {
48
- let colType: "varchar" | "text" | "char" = "varchar";
49
- if (dt === "text" || dt === "citext") colType = "text";
50
- if (dt === "char" || dt === "character") colType = "char";
51
- const prop: StringProperty = {
52
- type: "string",
53
- name: prettifiedName,
54
- columnType: colType,
55
- validation: required ? { required: true } : undefined
56
- };
57
- if (isAutoId) {
58
- prop.isId = "manual";
59
- }
60
- return prop;
61
- }
62
-
63
- case "uuid": {
64
- const prop: StringProperty = {
65
- type: "string",
66
- name: prettifiedName,
67
- validation: required ? { required: true } : undefined
68
- };
69
- if (isAutoId) {
70
- prop.isId = "uuid";
71
- }
72
- return prop;
73
- }
74
-
75
- case "integer":
76
- case "bigint":
77
- case "smallint": {
78
- const colType = dt === "bigint" ? "bigint" : "integer";
79
- const prop: NumberProperty = {
80
- type: "number",
81
- name: prettifiedName,
82
- columnType: colType,
83
- validation: {
84
- ...(required ? { required: true } : {}),
85
- integer: true
86
- }
87
- };
88
- if (isAutoId) {
89
- prop.isId = "increment";
90
- }
91
- return prop;
92
- }
93
-
94
- case "serial":
95
- case "bigserial":
96
- case "smallserial": {
97
- const colType = dt === "bigserial" ? "bigserial" : "serial";
98
- return {
99
- type: "number",
100
- name: prettifiedName,
101
- columnType: colType,
102
- isId: "increment",
103
- validation: {
104
- ...(required ? { required: true } : {}),
105
- integer: true
106
- }
107
- } as NumberProperty;
108
- }
109
-
110
- case "numeric":
111
- case "decimal":
112
- case "real":
113
- case "double precision": {
114
- let colType: "numeric" | "real" | "double precision" = "numeric";
115
- if (dt === "real") colType = "real";
116
- if (dt === "double precision") colType = "double precision";
117
- return {
118
- type: "number",
119
- name: prettifiedName,
120
- columnType: colType,
121
- validation: required ? { required: true } : undefined
122
- };
123
- }
124
-
125
- case "boolean":
126
- return {
127
- type: "boolean",
128
- name: prettifiedName,
129
- validation: required ? { required: true } : undefined
130
- };
131
-
132
- case "timestamp with time zone":
133
- case "timestamp without time zone":
134
- case "timestamp":
135
- case "timestamptz":
136
- case "date":
137
- case "time with time zone":
138
- case "time without time zone":
139
- case "time": {
140
- let colType: "timestamp" | "date" | "time" = "timestamp";
141
- if (dt.startsWith("date")) colType = "date";
142
- if (dt.startsWith("time ") || dt === "time") colType = "time";
143
- return {
144
- type: "date",
145
- name: prettifiedName,
146
- columnType: colType,
147
- validation: required ? { required: true } : undefined
148
- };
149
- }
150
-
151
- case "jsonb":
152
- case "json":
153
- return {
154
- type: "map",
155
- name: prettifiedName,
156
- columnType: dt === "jsonb" ? "jsonb" : "json",
157
- keyValue: true,
158
- properties: {}
159
- };
160
-
161
- case "array":
162
- case "ARRAY": {
163
- let innerType = "string";
164
- let colType: ArrayProperty["columnType"] = undefined;
165
- if (udt_name === "_text" || udt_name === "_varchar") {
166
- innerType = "string";
167
- colType = "text[]";
168
- } else if (udt_name === "_int4" || udt_name === "_int2" || udt_name === "_int8") {
169
- innerType = "number";
170
- colType = "integer[]";
171
- } else if (udt_name === "_bool") {
172
- innerType = "boolean";
173
- colType = "boolean[]";
174
- } else if (udt_name === "_numeric") {
175
- innerType = "number";
176
- colType = "numeric[]";
177
- }
178
- return {
179
- type: "array",
180
- name: prettifiedName,
181
- columnType: colType,
182
- of: { type: innerType }
183
- } as ArrayProperty;
184
- }
185
-
186
- default:
187
- // Fallback: treat unknown types as string
188
- return {
189
- type: "string",
190
- name: prettifiedName,
191
- validation: required ? { required: true } : undefined
192
- };
193
- }
194
- }
195
-
196
- /**
197
- * Builds a partial AdminCollection from PostgreSQL table metadata.
198
- * This is used when creating a new collection from an existing database table.
199
- */
200
- export function buildCollectionFromTableMetadata(
201
- tableName: string,
202
- metadata: TableMetadata
203
- ): Partial<AdminCollection> {
204
- const properties: Record<string, Property> = {};
205
- const propertiesOrder: string[] = [];
206
- const relations: any[] = []; // In the builder/editor, target can be a string path before hydration
207
- const securityRules: any[] = [];
208
-
209
- // Parse columns
210
- for (const column of metadata.columns) {
211
- const property = pgTypeToRebaseProperty(column);
212
- if (property) {
213
- // Remove undefined keys so we don't output "validation: undefined"
214
- const propRecord = property as unknown as Record<string, unknown>;
215
- Object.keys(propRecord).forEach(key => propRecord[key] === undefined && delete propRecord[key]);
216
-
217
- properties[column.column_name] = property;
218
- propertiesOrder.push(column.column_name);
219
- }
220
- }
221
-
222
- // Parse Outgoing Foreign Keys -> Many-to-One / One-to-One
223
- if (metadata.foreignKeys) {
224
- for (const fk of metadata.foreignKeys) {
225
- const relName = fk.column_name.endsWith("_id") ? fk.column_name.substring(0, fk.column_name.length - 3) : fk.column_name;
226
- relations.push({
227
- id: fk.column_name,
228
- relationName: relName,
229
- target: fk.foreign_table_name, // Will be hydrated later
230
- cardinality: "one",
231
- direction: "owning",
232
- localKey: fk.column_name
233
- });
234
- }
235
- }
236
-
237
- // Parse Incoming Junctions -> Many-to-Many
238
- if (metadata.junctions) {
239
- for (const junction of metadata.junctions) {
240
- const relName = junction.target_table_name; // E.g., 'roles'
241
- relations.push({
242
- id: junction.target_table_name + "_relation",
243
- relationName: relName,
244
- target: junction.target_table_name, // Will be hydrated later
245
- cardinality: "many",
246
- direction: "owning",
247
- through: {
248
- table: junction.junction_table_name,
249
- sourceColumn: junction.source_column_name,
250
- targetColumn: junction.target_column_name
251
- }
252
- });
253
- }
254
- }
255
-
256
- // Parse RLS Policies
257
- if (metadata.policies) {
258
- for (const policy of metadata.policies) {
259
- // Attempt to map typical cmds to operations.
260
- // Postgres cmd: SELECT, INSERT, UPDATE, DELETE, ALL
261
- let operations: string[] = [];
262
- switch (policy.cmd) {
263
- case "ALL": operations = ["read", "create", "update", "delete"]; break;
264
- case "SELECT": operations = ["read"]; break;
265
- case "INSERT": operations = ["create"]; break;
266
- case "UPDATE": operations = ["update"]; break;
267
- case "DELETE": operations = ["delete"]; break;
268
- }
269
- securityRules.push({
270
- name: policy.policy_name,
271
- operations,
272
- // roles is string[] e.g., ["public", "authenticated"]
273
- roles: policy.roles ?? [],
274
- qual: policy.qual,
275
- with_check: policy.with_check
276
- });
277
- }
278
- }
279
-
280
- const prettifiedName = prettifyIdentifier(tableName);
281
-
282
- return {
283
- name: prettifiedName,
284
- slug: tableName,
285
- table: tableName,
286
- properties: properties as PostgresProperties,
287
- propertiesOrder,
288
- ...(relations.length > 0 ? { relations } : {}),
289
- ...(securityRules.length > 0 ? { securityRules } : {})
290
- };
291
- }
@@ -1,133 +0,0 @@
1
- import { Decoration, DecorationSet, EditorView } from "prosemirror-view";
2
- import { Plugin, PluginKey } from "prosemirror-state";
3
- import { schema } from "../../schema";
4
-
5
- export type UploadFn = (image: File) => Promise<string>;
6
-
7
- export async function onFileRead(view: EditorView, readerEvent: ProgressEvent<FileReader>, pos: number, upload: UploadFn, image: File) {
8
- const state = view.state as typeof view.state & { plugins: Plugin[] };
9
- const plugin = state.plugins.find((p: Plugin) => p.spec.key === ImagePluginKey);
10
- if (!plugin) {
11
- console.error("Image plugin not found");
12
- return;
13
- }
14
- let decorationSet = plugin.getState(view.state);
15
-
16
- const placeholder = document.createElement("div");
17
- const imageElement = document.createElement("img");
18
- // basic styling for loading state
19
- imageElement.setAttribute("class", "opacity-40 rounded-lg border");
20
- imageElement.src = readerEvent.target?.result as string;
21
- placeholder.appendChild(imageElement);
22
-
23
- const deco = Decoration.widget(pos, placeholder);
24
- decorationSet = decorationSet?.add(view.state.doc, [deco]);
25
- view.dispatch(view.state.tr.setMeta(plugin, { decorationSet }));
26
-
27
- // Image Upload Logic
28
- const src = await upload(image);
29
- console.debug("Uploaded image", src);
30
-
31
- // Replace placeholder with actual image
32
- const imageNode = schema.nodes.image.create({ src });
33
- const tr = view.state.tr.replaceWith(pos, pos, imageNode);
34
-
35
- // Remove placeholder decoration
36
- decorationSet = decorationSet?.remove([deco]);
37
- tr.setMeta(plugin, { decorationSet });
38
- view.dispatch(tr);
39
- }
40
-
41
- export const ImagePluginKey = new PluginKey("imagePlugin");
42
-
43
- export const createDropImagePlugin = (upload: UploadFn): Plugin => {
44
- const plugin: Plugin = new Plugin({
45
- key: ImagePluginKey,
46
- state: {
47
- // Initialize the plugin state with an empty DecorationSet
48
- init: () => DecorationSet.empty,
49
- // Apply transactions to update the state
50
- apply: (tr, old) => {
51
- // Handle custom transaction steps that update decorations
52
- const meta = tr.getMeta(plugin);
53
- if (meta && meta.decorationSet) {
54
- return meta.decorationSet;
55
- }
56
- // Map decorations to the new document structure
57
- return old.map(tr.mapping, tr.doc);
58
- }
59
- },
60
- props: {
61
- handleDOMEvents: {
62
- drop: (view, event) => {
63
- if (!event.dataTransfer?.files || event.dataTransfer?.files.length === 0) {
64
- return false;
65
- }
66
- event.preventDefault();
67
-
68
- const files = Array.from(event.dataTransfer.files);
69
- const images = files.filter(file => /image/i.test(file.type));
70
-
71
- if (images.length === 0) {
72
- console.debug("No images found in dropped files");
73
- return false;
74
- }
75
-
76
- images.forEach(image => {
77
- const position = view.posAtCoords({
78
- left: event.clientX,
79
- top: event.clientY
80
- });
81
- if (!position) return;
82
-
83
- const reader = new FileReader();
84
- reader.onload = async (readerEvent) => {
85
- await onFileRead(view, readerEvent, position.pos, upload, image);
86
- };
87
- reader.readAsDataURL(image);
88
- });
89
-
90
- return true;
91
- }
92
- },
93
- handlePaste(view, event, slice) {
94
- const items = Array.from(event.clipboardData?.items || []);
95
- const pos = view.state.selection.from;
96
- let anyImageFound = false;
97
-
98
- items.forEach((item) => {
99
- const image = item.getAsFile();
100
- if (image && /image/i.test(item.type)) {
101
- anyImageFound = true;
102
- const reader = new FileReader();
103
-
104
- reader.onload = async (readerEvent) => {
105
- await onFileRead(view, readerEvent, pos, upload, image);
106
- };
107
- reader.readAsDataURL(image);
108
- }
109
- });
110
-
111
- return anyImageFound;
112
- },
113
- decorations(state) {
114
- return plugin.getState(state);
115
- }
116
- },
117
- view(editorView) {
118
- // This is needed to immediately apply the decoration updates
119
- return {
120
- update(view, prevState) {
121
- const prevDecos = plugin.getState(prevState);
122
- const newDecos = plugin.getState(view.state);
123
-
124
- if (prevDecos !== newDecos) {
125
- view.updateState(view.state);
126
- }
127
- }
128
- };
129
- }
130
- });
131
-
132
- return plugin;
133
- };