@rebasepro/core 0.0.1-canary.eae7889 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/dist/components/BootstrapAdminBanner.d.ts +4 -0
  2. package/dist/components/LoginView/LoginView.d.ts +22 -0
  3. package/dist/components/common/useDataTableController.d.ts +3 -3
  4. package/dist/components/index.d.ts +1 -0
  5. package/dist/hooks/data/useRelationSelector.d.ts +2 -2
  6. package/dist/hooks/index.d.ts +1 -0
  7. package/dist/hooks/useCollapsedGroups.d.ts +16 -1
  8. package/dist/hooks/useResolvedComponent.d.ts +47 -0
  9. package/dist/index.es.js +333 -121
  10. package/dist/index.es.js.map +1 -1
  11. package/dist/index.umd.js +330 -118
  12. package/dist/index.umd.js.map +1 -1
  13. package/dist/vitePlugin.d.ts +28 -1
  14. package/dist/vitePlugin.js +42 -0
  15. package/package.json +7 -8
  16. package/src/components/BootstrapAdminBanner.tsx +66 -0
  17. package/src/components/LoginView/LoginView.tsx +73 -42
  18. package/src/components/UserSelectPopover.tsx +8 -34
  19. package/src/components/common/useColumnsIds.tsx +16 -16
  20. package/src/components/common/useDataTableController.tsx +30 -18
  21. package/src/components/index.tsx +1 -1
  22. package/src/core/Rebase.tsx +20 -14
  23. package/src/hooks/data/useRelationSelector.tsx +6 -6
  24. package/src/hooks/index.tsx +1 -0
  25. package/src/hooks/useCollapsedGroups.ts +48 -6
  26. package/src/hooks/useRebaseContext.tsx +11 -6
  27. package/src/hooks/useResolvedComponent.tsx +157 -0
  28. package/src/hooks/useStudioBridge.tsx +2 -1
  29. package/src/locales/de.ts +4 -0
  30. package/src/locales/en.ts +6 -0
  31. package/src/locales/es.ts +4 -0
  32. package/src/locales/fr.ts +4 -0
  33. package/src/locales/hi.ts +4 -0
  34. package/src/locales/it.ts +4 -0
  35. package/src/locales/pt.ts +4 -0
  36. package/src/util/previews.ts +16 -7
  37. package/src/vitePlugin.ts +87 -1
@@ -116,20 +116,21 @@ export function Rebase<USER extends User>(props: RebaseProps<USER>) {
116
116
  }
117
117
 
118
118
  // 2. Auto-derive from the client's WebSocket connection (Rebase backend)
119
- const ws = (client as any)?.ws;
120
- if (ws && typeof ws.executeSql === "function") {
119
+ const ws = client?.ws;
120
+ if (ws && typeof (ws as Record<string, unknown>).executeSql === "function") {
121
+ const wsAdmin = ws as import("@rebasepro/types").DatabaseAdmin;
121
122
  return {
122
- executeSql: ws.executeSql.bind(ws),
123
- fetchAvailableDatabases: ws.fetchAvailableDatabases?.bind(ws),
124
- fetchAvailableRoles: ws.fetchAvailableRoles?.bind(ws),
125
- fetchCurrentDatabase: ws.fetchCurrentDatabase?.bind(ws),
126
- fetchUnmappedTables: ws.fetchUnmappedTables?.bind(ws),
127
- fetchTableMetadata: ws.fetchTableMetadata?.bind(ws),
123
+ executeSql: wsAdmin.executeSql!.bind(wsAdmin),
124
+ fetchAvailableDatabases: wsAdmin.fetchAvailableDatabases?.bind(wsAdmin),
125
+ fetchAvailableRoles: wsAdmin.fetchAvailableRoles?.bind(wsAdmin),
126
+ fetchCurrentDatabase: wsAdmin.fetchCurrentDatabase?.bind(wsAdmin),
127
+ fetchUnmappedTables: wsAdmin.fetchUnmappedTables?.bind(wsAdmin),
128
+ fetchTableMetadata: wsAdmin.fetchTableMetadata?.bind(wsAdmin),
128
129
  // Branch admin capabilities
129
- ...(typeof ws.createBranch === "function" ? {
130
- createBranch: ws.createBranch.bind(ws),
131
- deleteBranch: ws.deleteBranch.bind(ws),
132
- listBranches: ws.listBranches.bind(ws)
130
+ ...(typeof wsAdmin.createBranch === "function" ? {
131
+ createBranch: wsAdmin.createBranch.bind(wsAdmin),
132
+ deleteBranch: wsAdmin.deleteBranch!.bind(wsAdmin),
133
+ listBranches: wsAdmin.listBranches!.bind(wsAdmin)
133
134
  } : {})
134
135
  };
135
136
  }
@@ -224,7 +225,7 @@ export function Rebase<USER extends User>(props: RebaseProps<USER>) {
224
225
  </RebaseI18nProvider>
225
226
  );
226
227
 
227
- const resolvedApiUrl = apiUrl ?? (client as any)?.baseUrl;
228
+ const resolvedApiUrl = apiUrl ?? client?.baseUrl;
228
229
 
229
230
  if (resolvedApiUrl) {
230
231
  return (
@@ -255,7 +256,12 @@ function RebaseInternal({
255
256
  }) : children;
256
257
 
257
258
  const plugins = customizationController.plugins;
258
- if (!loading && plugins && plugins.length > 0) {
259
+ const authController = context.authController;
260
+ const authReady = !loading
261
+ && !authController.authLoading
262
+ && (Boolean(authController.user) || authController.loginSkipped);
263
+
264
+ if (authReady && plugins && plugins.length > 0) {
259
265
  return (
260
266
  <PluginProviderStack
261
267
  plugins={plugins}
@@ -22,7 +22,7 @@ export interface UseRelationSelectorProps<M extends Record<string, any> = any> {
22
22
  /**
23
23
  * Force filter to be applied to the relation search
24
24
  */
25
- forceFilter?: FilterValues<string>;
25
+ fixedFilter?: FilterValues<string>;
26
26
  /**
27
27
  * Page size for pagination
28
28
  */
@@ -60,7 +60,7 @@ export function useRelationSelector<M extends Record<string, any> = any>(
60
60
  {
61
61
  path,
62
62
  collection,
63
- forceFilter,
63
+ fixedFilter,
64
64
  pageSize = DEFAULT_PAGE_SIZE,
65
65
  getLabelFromEntity,
66
66
  getDescriptionFromEntity,
@@ -138,10 +138,10 @@ export function useRelationSelector<M extends Record<string, any> = any>(
138
138
  setError(undefined);
139
139
  setLoading(true);
140
140
 
141
- // Convert forceFilter to PostgREST where clause
141
+ // Convert fixedFilter to PostgREST where clause
142
142
  const whereMap: Record<string, string> = {};
143
- if (forceFilter) {
144
- Object.entries(forceFilter).forEach(([key, value]) => {
143
+ if (fixedFilter) {
144
+ Object.entries(fixedFilter).forEach(([key, value]) => {
145
145
  if (value && Array.isArray(value)) {
146
146
  const [op, val] = value;
147
147
  const postgrestOp = op === "==" ? "eq" : op === "!=" ? "neq" : op === ">" ? "gt" : op === ">=" ? "gte" : op === "<" ? "lt" : op === "<=" ? "lte" : op === "in" ? "in" : op === "not-in" ? "nin" : op === "array-contains" ? "cs" : op === "array-contains-any" ? "csa" : "eq";
@@ -196,7 +196,7 @@ export function useRelationSelector<M extends Record<string, any> = any>(
196
196
  }
197
197
 
198
198
  unsubscribeRef.current = unsubscribe || null;
199
- }, [dataClient, path, forceFilter, limit, currentSearch, entityToRelationItem, cleanupSubscription, setLoading]);
199
+ }, [dataClient, path, fixedFilter, limit, currentSearch, entityToRelationItem, cleanupSubscription, setLoading]);
200
200
 
201
201
  // Search function with debouncing
202
202
  const search = useCallback((searchString: string) => {
@@ -45,3 +45,4 @@ export * from "./useRebaseClient";
45
45
 
46
46
  export * from "./useAnalyticsController";
47
47
  export * from "./useUserConfigurationPersistence";
48
+ export * from "./useResolvedComponent";
@@ -7,10 +7,18 @@ const STORAGE_KEY_PREFIX = "rebase-collapsed-groups";
7
7
  * with localStorage persistence. Automatically cleans up stale group entries
8
8
  * when groups are removed from the navigation.
9
9
  *
10
+ * Groups that have never been toggled by the user fall back to
11
+ * `defaults[groupName]` (driven by `collapsedByDefault` in config).
12
+ *
10
13
  * @param groupNames - Array of group names to track
11
14
  * @param namespace - Namespace for localStorage key (e.g., "home", "drawer") to allow independent state
15
+ * @param defaults - Optional map of group name → collapsed boolean from config
12
16
  */
13
- export function useCollapsedGroups(groupNames: string[], namespace = "default") {
17
+ export function useCollapsedGroups(
18
+ groupNames: string[],
19
+ namespace = "default",
20
+ defaults?: Record<string, boolean>
21
+ ) {
14
22
  const storageKey = `${STORAGE_KEY_PREFIX}-${namespace}`;
15
23
 
16
24
  // Load collapsed groups from localStorage on mount
@@ -57,13 +65,23 @@ export function useCollapsedGroups(groupNames: string[], namespace = "default")
57
65
  }, [groupNames]);
58
66
 
59
67
  const isGroupCollapsed = useCallback((name: string) => {
60
- return !!collapsedGroups[name];
61
- }, [collapsedGroups]);
68
+ // If the user has explicitly toggled this group, use that value
69
+ if (name in collapsedGroups) {
70
+ return collapsedGroups[name];
71
+ }
72
+ // Otherwise fall back to the config default
73
+ return defaults?.[name] ?? false;
74
+ }, [collapsedGroups, defaults]);
62
75
 
63
76
  const toggleGroupCollapsed = useCallback((name: string) => {
64
- setCollapsedGroups(prev => ({ ...prev,
65
- [name]: !prev[name] }));
66
- }, []);
77
+ setCollapsedGroups(prev => {
78
+ // Resolve current effective state (explicit or default)
79
+ const currentlyCollapsed = name in prev
80
+ ? prev[name]
81
+ : (defaults?.[name] ?? false);
82
+ return { ...prev, [name]: !currentlyCollapsed };
83
+ });
84
+ }, [defaults]);
67
85
 
68
86
  return useMemo(() => ({
69
87
  isGroupCollapsed,
@@ -71,3 +89,27 @@ export function useCollapsedGroups(groupNames: string[], namespace = "default")
71
89
  }), [isGroupCollapsed, toggleGroupCollapsed]);
72
90
  }
73
91
 
92
+ /**
93
+ * Build a defaults map from navigationGroupMappings for a given namespace.
94
+ * Returns a Record<groupName, collapsed> that can be passed to useCollapsedGroups.
95
+ */
96
+ export function buildCollapsedDefaults(
97
+ mappings: Array<{ name: string; collapsedByDefault?: boolean | { drawer?: boolean; home?: boolean } }> | undefined,
98
+ namespace: "drawer" | "home"
99
+ ): Record<string, boolean> {
100
+ if (!mappings) return {};
101
+ const result: Record<string, boolean> = {};
102
+ for (const mapping of mappings) {
103
+ const val = mapping.collapsedByDefault;
104
+ if (val === undefined) continue;
105
+ if (typeof val === "boolean") {
106
+ result[mapping.name] = val;
107
+ } else {
108
+ const ns = val[namespace];
109
+ if (ns !== undefined) {
110
+ result[mapping.name] = ns;
111
+ }
112
+ }
113
+ }
114
+ return result;
115
+ }
@@ -1,5 +1,6 @@
1
1
  import { AuthController, RebaseContext, User } from "@rebasepro/types";
2
2
  import { useAuthController } from "./useAuthController";
3
+ import { useRebaseClient } from "./useRebaseClient";
3
4
  import { useData } from "./data/useData";
4
5
  import { useStorageSource } from "./useStorageSource";
5
6
  import { useSnackbarController } from "./useSnackbarController";
@@ -11,9 +12,7 @@ import { useEffectiveRoleController } from "./useEffectiveRoleController";
11
12
  import React, { useEffect, useContext } from "react";
12
13
  import { useInternalUserManagementController } from "./useInternalUserManagementController";
13
14
 
14
- // Temporary context for databaseAdmin - assuming we might add one, or we need to get it from Rebase.tsx.
15
- // Wait, `databaseAdmin` hasn't been added to a context yet. Let's add it to a context in Rebase.tsx later.
16
- // Let's create a placeholder for databaseAdmin context access.
15
+ // DatabaseAdmin is provided by <Rebase> via DatabaseAdminContext.
17
16
  import { DatabaseAdminContext } from "../contexts/DatabaseAdminContext";
18
17
 
19
18
  /**
@@ -27,6 +26,8 @@ import { DatabaseAdminContext } from "../contexts/DatabaseAdminContext";
27
26
  */
28
27
  export const useRebaseContext = <USER extends User = User, AuthControllerType extends AuthController<USER> = AuthController<USER>>(): RebaseContext<USER, AuthControllerType> => {
29
28
 
29
+ const client = useRebaseClient<any>();
30
+
30
31
  const authController = useAuthController<USER, AuthControllerType>();
31
32
  const data = useData();
32
33
  const storageSource = useStorageSource();
@@ -52,7 +53,8 @@ export const useRebaseContext = <USER extends User = User, AuthControllerType ex
52
53
  analyticsController,
53
54
  userManagement,
54
55
  effectiveRoleController,
55
- databaseAdmin
56
+ databaseAdmin,
57
+ client: client! // Client should be provided
56
58
  });
57
59
 
58
60
  React.useEffect(() => {
@@ -67,9 +69,12 @@ export const useRebaseContext = <USER extends User = User, AuthControllerType ex
67
69
  analyticsController,
68
70
  userManagement,
69
71
  effectiveRoleController,
70
- databaseAdmin
72
+ databaseAdmin,
73
+ client: client!
71
74
  };
72
- }, [authController, dialogsController, effectiveRoleController, data, databaseAdmin]);
75
+ }, [authController, data, storageSource, snackbarController, userConfigPersistence,
76
+ dialogsController, customizationController, analyticsController, userManagement,
77
+ effectiveRoleController, databaseAdmin, client]);
73
78
 
74
79
  return rebaseContextRef.current;
75
80
  }
@@ -0,0 +1,157 @@
1
+ import React, { lazy, useMemo } from "react";
2
+ import type { ComponentRef, LazyComponentRef } from "@rebasepro/types";
3
+ import { isLazyComponentRef } from "@rebasepro/types";
4
+
5
+ /**
6
+ * Internal cache for resolved lazy components.
7
+ *
8
+ * `React.lazy()` must return the SAME wrapper across renders — if we call
9
+ * `lazy(loader)` on every render, React will treat each result as a new
10
+ * component type and unmount/remount on each render cycle.
11
+ *
12
+ * This WeakMap keeps a stable mapping from a `ComponentRef` (object/function)
13
+ * to the `React.lazy()` wrapper it produced. Strings are keyed by a separate
14
+ * plain Map since they can't be WeakMap keys.
15
+ */
16
+ const lazyCache = new WeakMap<object | Function, React.ComponentType<any>>();
17
+
18
+ /**
19
+ * Resolves a `ComponentRef` into a renderable `React.ComponentType`.
20
+ *
21
+ * This hook handles all three forms of `ComponentRef`:
22
+ *
23
+ * 1. **`LazyComponentRef`** (produced by the Vite plugin from string paths):
24
+ * Wraps the lazy loader with `React.lazy()` for automatic code-splitting.
25
+ *
26
+ * 2. **`() => Promise<{ default: ComponentType }>`** (manual lazy import):
27
+ * Also wraps with `React.lazy()`. Distinguished from regular components
28
+ * by checking that the function has no parameters and is not a known
29
+ * React internal (no `$$typeof`).
30
+ *
31
+ * 3. **Direct `React.ComponentType`**:
32
+ * Returned as-is.
33
+ *
34
+ * If the ref is `undefined` or a raw string (which should never happen at
35
+ * runtime in the browser since the Vite plugin transforms strings), returns `undefined`.
36
+ *
37
+ * The returned component is stable across re-renders as long as the `ref`
38
+ * is referentially stable.
39
+ *
40
+ * **Usage:** Wrap the rendered component in `<Suspense>` to handle the
41
+ * loading state of lazy components.
42
+ *
43
+ * @example
44
+ * ```tsx
45
+ * const ResolvedField = useResolvedComponent(property.ui?.Field);
46
+ * if (!ResolvedField) return null;
47
+ * return (
48
+ * <Suspense fallback={<CircularProgress />}>
49
+ * <ResolvedField {...fieldProps} />
50
+ * </Suspense>
51
+ * );
52
+ * ```
53
+ */
54
+ export function useResolvedComponent<P = unknown>(
55
+ ref: ComponentRef<P> | undefined
56
+ ): React.ComponentType<P> | undefined {
57
+ return useMemo(() => resolveComponentRef(ref), [ref]);
58
+ }
59
+
60
+ /**
61
+ * Wraps a loader function with `React.lazy()`, caching the result so the
62
+ * same loader always returns the same lazy component identity.
63
+ */
64
+ function getOrCreateLazy<P>(
65
+ key: object | Function,
66
+ loader: () => Promise<{ default: React.ComponentType<P> }>
67
+ ): React.ComponentType<P> {
68
+ const cached = lazyCache.get(key);
69
+ if (cached) return cached as React.ComponentType<P>;
70
+
71
+ const LazyComponent = lazy(loader) as unknown as React.ComponentType<P>;
72
+ lazyCache.set(key, LazyComponent);
73
+ return LazyComponent;
74
+ }
75
+
76
+ /**
77
+ * Pure function version of the resolver, for use outside React components.
78
+ * Same resolution logic as `useResolvedComponent`.
79
+ *
80
+ * Results are cached per reference identity — calling this multiple times
81
+ * with the same `ref` object returns the same `React.ComponentType`.
82
+ */
83
+ export function resolveComponentRef<P = unknown>(
84
+ ref: ComponentRef<P> | undefined
85
+ ): React.ComponentType<P> | undefined {
86
+ if (ref == null) return undefined;
87
+
88
+ // 1. String — should not happen at runtime (Vite transforms them).
89
+ // Log a warning and bail out.
90
+ if (typeof ref === "string") {
91
+ console.warn(
92
+ `[Rebase] Encountered a raw string ComponentRef ("${ref}") at runtime. ` +
93
+ "This usually means the Vite transform plugin did not process this file. " +
94
+ "Ensure the file is inside the configured collectionsDir."
95
+ );
96
+ return undefined;
97
+ }
98
+
99
+ // 2. LazyComponentRef — produced by the Vite plugin from string paths.
100
+ // The object has { __rebaseLazy: true, load: () => import(...) }.
101
+ if (isLazyComponentRef(ref)) {
102
+ return getOrCreateLazy<P>(
103
+ ref as unknown as object,
104
+ () => (ref as LazyComponentRef<P>).load()
105
+ );
106
+ }
107
+
108
+ // 3. Function — either a React component or a lazy import loader.
109
+ if (typeof ref === "function") {
110
+ const fn = ref as Function;
111
+
112
+ // Class components (React.Component / PureComponent) have this flag
113
+ if (fn.prototype?.isReactComponent) {
114
+ return ref as React.ComponentType<P>;
115
+ }
116
+
117
+ // React internals (forwardRef, memo, etc.) carry $$typeof
118
+ if ("$$typeof" in fn) {
119
+ return ref as React.ComponentType<P>;
120
+ }
121
+
122
+ // A function with declared parameters (fn.length > 0) is a regular
123
+ // component that accepts props — return as-is.
124
+ if (fn.length > 0) {
125
+ return ref as React.ComponentType<P>;
126
+ }
127
+
128
+ // Zero-parameter function — could be a React component with no props
129
+ // OR a lazy loader `() => import(...)`. We distinguish by checking
130
+ // if `fn.name` looks like a component (starts with uppercase) or is
131
+ // an anonymous arrow.
132
+ //
133
+ // Convention: named function components always start with an
134
+ // uppercase letter. Dynamic import wrappers are typically anonymous
135
+ // arrows or have lowercase names.
136
+ const name = fn.name;
137
+ if (name && /^[A-Z]/.test(name)) {
138
+ // Named component (e.g. `function MyField() { ... }`) — return as-is
139
+ return ref as React.ComponentType<P>;
140
+ }
141
+
142
+ // Treat as a lazy loader — wrap with React.lazy, cached by identity.
143
+ return getOrCreateLazy<P>(
144
+ fn,
145
+ ref as () => Promise<{ default: React.ComponentType<P> }>
146
+ );
147
+ }
148
+
149
+ // 4. React wrapper objects (React.forwardRef, React.memo) —
150
+ // These are objects with $$typeof but are not functions.
151
+ if (typeof ref === "object" && "$$typeof" in (ref as object)) {
152
+ return ref as unknown as React.ComponentType<P>;
153
+ }
154
+
155
+ // 5. Unknown shape — return undefined to be safe
156
+ return undefined;
157
+ }
@@ -42,7 +42,8 @@ const NOOP_COLLECTION_REGISTRY: CollectionRegistryController = {
42
42
  getCollection: () => undefined,
43
43
  getRawCollection: () => undefined,
44
44
  getParentReferencesFromPath: () => [],
45
- getParentCollectionIds: () => [],
45
+ getParentCollectionSlugs: () => [],
46
+ getParentEntityIds: () => [],
46
47
  convertIdsToPaths: () => [],
47
48
  initialised: false
48
49
  };
package/src/locales/de.ts CHANGED
@@ -44,6 +44,7 @@ export const de: RebaseTranslations = {
44
44
  all_entries_loaded: "Alle {{count}} Einträge geladen",
45
45
  create_your_first_entry: "Erstellen Sie Ihren ersten Eintrag",
46
46
  no_results_filter_sort: "Keine Ergebnisse mit angewendetem Filter/Sortierung",
47
+ no_results_search: "Keine Ergebnisse gefunden für \"{{search}}\"",
47
48
  add: "Hinzufügen",
48
49
  remove: "Entfernen",
49
50
  multiple_entities: "Mehrere Entitäten",
@@ -127,6 +128,9 @@ export const de: RebaseTranslations = {
127
128
  delete_user_confirmation: "Sind Sie sicher, dass Sie diesen Benutzer löschen möchten?",
128
129
  create_your_users_and_roles: "Erstellen Sie Ihre Benutzer und Rollen",
129
130
  no_users_or_roles_defined: "Sie haben keine Benutzer oder Rollen definiert. Sie können Standardrollen erstellen und den aktuellen Benutzer als Administrator hinzufügen.",
131
+ no_permission_to_view_users: "Sie haben keine Berechtigung, Benutzer anzuzeigen",
132
+ no_permission_to_view_roles: "Sie haben keine Berechtigung, Rollen anzuzeigen",
133
+ no_permission_description: "Wenden Sie sich an einen Administrator, wenn Sie Zugang zu diesem Bereich benötigen.",
130
134
  save_before_changing_schema: "Sie müssen das Dokument speichern, bevor Sie das Schema ändern können",
131
135
  edit_schema_for_this_form: "Schema für dieses Formular bearbeiten",
132
136
  no_permissions_to_edit_collection: "Sie haben keine Berechtigung, diese Sammlung zu bearbeiten",
package/src/locales/en.ts CHANGED
@@ -52,6 +52,7 @@ export const en: RebaseTranslations = {
52
52
  all_entries_loaded: "All {{count}} entries loaded",
53
53
  create_your_first_entry: "Create your first entry",
54
54
  no_results_filter_sort: "No results with the applied filter/sort",
55
+ no_results_search: "No results found for \"{{search}}\"",
55
56
  add: "Add",
56
57
  remove: "Remove",
57
58
  multiple_entities: "Multiple entities",
@@ -487,6 +488,11 @@ export const en: RebaseTranslations = {
487
488
  reset_password: "Reset Password",
488
489
  reset_password_success: "Password reset successfully",
489
490
  reset_password_confirmation: "Are you sure you want to reset this user's password?",
491
+
492
+ /** Permission-denied empty states */
493
+ no_permission_to_view_users: "You don't have permission to view users",
494
+ no_permission_to_view_roles: "You don't have permission to view roles",
495
+ no_permission_description: "Contact an administrator if you need access to this section.",
490
496
  error_resetting_password: "Error resetting password",
491
497
 
492
498
  /** Editor table-bubble */
package/src/locales/es.ts CHANGED
@@ -52,6 +52,7 @@ export const es: RebaseTranslations = {
52
52
  all_entries_loaded: "Todas las {{count}} entradas cargadas",
53
53
  create_your_first_entry: "Crea tu primera entrada",
54
54
  no_results_filter_sort: "No hay resultados con el filtro/orden aplicado",
55
+ no_results_search: "No se encontraron resultados para \"{{search}}\"",
55
56
  add: "Añadir",
56
57
  remove: "Quitar",
57
58
  multiple_entities: "Múltiples entidades",
@@ -133,6 +134,9 @@ export const es: RebaseTranslations = {
133
134
  delete_user_confirmation: "¿Estás seguro de que quieres eliminar a este usuario?",
134
135
  create_your_users_and_roles: "Crea tus usuarios y roles",
135
136
  no_users_or_roles_defined: "No tienes usuarios ni roles. Puedes crear los roles por defecto y añadirte a ti mismo como administrador.",
137
+ no_permission_to_view_users: "No tienes permisos para ver los usuarios",
138
+ no_permission_to_view_roles: "No tienes permisos para ver los roles",
139
+ no_permission_description: "Contacta a un administrador si necesitas acceso a esta sección.",
136
140
  save_before_changing_schema: "Debes guardar el documento antes de cambiar el esquema",
137
141
  edit_schema_for_this_form: "Editar esquema para este formulario",
138
142
  no_permissions_to_edit_collection: "No tienes permisos para editar esta colección",
package/src/locales/fr.ts CHANGED
@@ -44,6 +44,7 @@ export const fr: RebaseTranslations = {
44
44
  all_entries_loaded: "Toutes les {{count}} entrées chargées",
45
45
  create_your_first_entry: "Créez votre première entrée",
46
46
  no_results_filter_sort: "Aucun résultat avec le filtre/tri appliqué",
47
+ no_results_search: "Aucun résultat trouvé pour \"{{search}}\"",
47
48
  add: "Ajouter",
48
49
  remove: "Supprimer",
49
50
  multiple_entities: "Entités multiples",
@@ -127,6 +128,9 @@ export const fr: RebaseTranslations = {
127
128
  delete_user_confirmation: "Êtes-vous sûr de vouloir supprimer cet utilisateur ?",
128
129
  create_your_users_and_roles: "Créez vos utilisateurs et rôles",
129
130
  no_users_or_roles_defined: "Vous n'avez défini aucun utilisateur ni rôle. Vous pouvez créer les rôles par défaut et ajouter l'utilisateur actuel en tant qu'administrateur.",
131
+ no_permission_to_view_users: "Vous n'avez pas la permission de voir les utilisateurs",
132
+ no_permission_to_view_roles: "Vous n'avez pas la permission de voir les rôles",
133
+ no_permission_description: "Contactez un administrateur si vous avez besoin d'accéder à cette section.",
130
134
  save_before_changing_schema: "Vous devez enregistrer le document avant de modifier le schéma",
131
135
  edit_schema_for_this_form: "Modifier le schéma de ce formulaire",
132
136
  no_permissions_to_edit_collection: "Vous n'avez pas l'autorisation de modifier cette collection",
package/src/locales/hi.ts CHANGED
@@ -44,6 +44,7 @@ export const hi: RebaseTranslations = {
44
44
  all_entries_loaded: "सभी {{count}} प्रविष्टियाँ लोड हो गईं",
45
45
  create_your_first_entry: "अपनी पहली प्रविष्टि बनाएं",
46
46
  no_results_filter_sort: "लागू किए गए फ़िल्टर/सॉर्ट के साथ कोई परिणाम नहीं",
47
+ no_results_search: "\"{{search}}\" के लिए कोई परिणाम नहीं मिला",
47
48
  add: "जोड़ें",
48
49
  remove: "हटाएं",
49
50
  multiple_entities: "एकाधिक इकाइयां",
@@ -127,6 +128,9 @@ export const hi: RebaseTranslations = {
127
128
  delete_user_confirmation: "क्या आप वाकई इस उपयोगकर्ता को हटाना चाहते हैं?",
128
129
  create_your_users_and_roles: "अपने उपयोगकर्ता और भूमिकाएँ बनाएँ",
129
130
  no_users_or_roles_defined: "आपके पास कोई उपयोगकर्ता या भूमिका परिभाषित नहीं है। आप डिफ़ॉल्ट भूमिकाएँ बना सकते हैं और वर्तमान उपयोगकर्ता को एडमिन के रूप में जोड़ सकते हैं।",
131
+ no_permission_to_view_users: "आपको उपयोगकर्ताओं को देखने की अनुमति नहीं है",
132
+ no_permission_to_view_roles: "आपको भूमिकाओं को देखने की अनुमति नहीं है",
133
+ no_permission_description: "यदि आपको इस अनुभाग तक पहुँच की आवश्यकता है तो किसी व्यवस्थापक से संपर्क करें।",
130
134
  save_before_changing_schema: "स्कीमा बदलने से पहले आपको दस्तावेज़ सहेजना होगा",
131
135
  edit_schema_for_this_form: "इस फॉर्म के लिए स्कीमा संपादित करें",
132
136
  no_permissions_to_edit_collection: "आपके पास इस संग्रह को संपादित करने की अनुमति नहीं है",
package/src/locales/it.ts CHANGED
@@ -44,6 +44,7 @@ export const it: RebaseTranslations = {
44
44
  all_entries_loaded: "Tutte le {{count}} voci caricate",
45
45
  create_your_first_entry: "Crea la tua prima voce",
46
46
  no_results_filter_sort: "Nessun risultato con il filtro/ordinamento applicato",
47
+ no_results_search: "Nessun risultato trovato per \"{{search}}\"",
47
48
  add: "Aggiungi",
48
49
  remove: "Rimuovi",
49
50
  multiple_entities: "Entità multiple",
@@ -127,6 +128,9 @@ export const it: RebaseTranslations = {
127
128
  delete_user_confirmation: "Sei sicuro di voler eliminare questo utente?",
128
129
  create_your_users_and_roles: "Crea i tuoi utenti e ruoli",
129
130
  no_users_or_roles_defined: "Non hai definito né utenti né ruoli. Puoi creare ruoli predefiniti e aggiungere l'utente corrente come amministratore.",
131
+ no_permission_to_view_users: "Non hai i permessi per visualizzare gli utenti",
132
+ no_permission_to_view_roles: "Non hai i permessi per visualizzare i ruoli",
133
+ no_permission_description: "Contatta un amministratore se hai bisogno di accedere a questa sezione.",
130
134
  save_before_changing_schema: "Devi salvare il documento prima di modificare lo schema",
131
135
  edit_schema_for_this_form: "Modifica lo schema per questo modulo",
132
136
  no_permissions_to_edit_collection: "Non hai i permessi per modificare questa collezione",
package/src/locales/pt.ts CHANGED
@@ -49,6 +49,7 @@ export const pt: RebaseTranslations = {
49
49
  all_entries_loaded: "Todos os {{count}} registos carregados",
50
50
  create_your_first_entry: "Crie o seu primeiro registo",
51
51
  no_results_filter_sort: "Sem resultados com o filtro/ordenação aplicado",
52
+ no_results_search: "Nenhum resultado encontrado para \"{{search}}\"",
52
53
  add: "Adicionar",
53
54
  remove: "Remover",
54
55
  multiple_entities: "Múltiplas entidades",
@@ -132,6 +133,9 @@ export const pt: RebaseTranslations = {
132
133
  delete_user_confirmation: "Tem a certeza de que quer eliminar este utilizador?",
133
134
  create_your_users_and_roles: "Crie os seus utilizadores e funções",
134
135
  no_users_or_roles_defined: "Não tem utilizadores ou funções definidas. Pode criar funções predefinidas e adicionar o utilizador atual como administrador.",
136
+ no_permission_to_view_users: "Não tem permissão para ver os utilizadores",
137
+ no_permission_to_view_roles: "Não tem permissão para ver as funções",
138
+ no_permission_description: "Contacte um administrador se precisar de acesso a esta secção.",
135
139
  save_before_changing_schema: "Precisa de guardar o documento antes de alterar o esquema",
136
140
  edit_schema_for_this_form: "Editar esquema para este formulário",
137
141
  no_permissions_to_edit_collection: "Não tem permissões para editar esta coleção",
@@ -36,7 +36,7 @@ export function getEntityPreviewKeys(
36
36
  if (listProperties && listProperties.length > 0) {
37
37
  return listProperties;
38
38
  } else {
39
- listProperties = allProperties;
39
+ listProperties = (targetCollection.propertiesOrder as string[]) || allProperties;
40
40
  return listProperties
41
41
  .filter(key => {
42
42
  const prop = targetCollection.properties[key];
@@ -54,16 +54,25 @@ export function getEntityTitlePropertyKey<M extends Record<string, any>>(collect
54
54
  if (collection.titleProperty) {
55
55
  return collection.titleProperty as string;
56
56
  }
57
- // find first text field property
58
- for (const key in collection.properties) {
57
+
58
+ const orderToSearch = (collection.propertiesOrder as string[]) || Object.keys(collection.properties);
59
+ let firstStringCandidate: string | undefined;
60
+
61
+ for (const key of orderToSearch) {
59
62
  const property = collection.properties[key];
60
- if (!isPropertyBuilder(property)) {
63
+ if (property && !isPropertyBuilder(property)) {
61
64
  const prop = property as Property;
62
- if (prop.type === "string" && !prop.multiline && !prop.markdown && !prop.storage && !prop.isId) {
63
- return key;
65
+ if (prop.type === "string" && !prop.ui?.multiline && !prop.ui?.markdown && !prop.storage && !prop.isId) {
66
+ if (!firstStringCandidate) {
67
+ firstStringCandidate = key;
68
+ }
69
+ const lowerKey = key.toLowerCase();
70
+ if (["name", "title", "label", "displayname", "username"].includes(lowerKey)) {
71
+ return key; // Immediate return if it's a strong title candidate
72
+ }
64
73
  }
65
74
  }
66
75
  }
67
- return undefined;
76
+ return firstStringCandidate;
68
77
  }
69
78