@rebasepro/core 0.0.1-canary.f81da60 → 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.
@@ -0,0 +1,4 @@
1
+ export interface BootstrapAdminBannerProps {
2
+ className?: string;
3
+ }
4
+ export declare function BootstrapAdminBanner({ className }: BootstrapAdminBannerProps): import("react/jsx-runtime").JSX.Element | null;
@@ -1,4 +1,26 @@
1
1
  import { ReactNode } from "react";
2
+ /** Google Identity Services SDK — injected by the GIS <script> tag. */
3
+ declare global {
4
+ interface Window {
5
+ google?: {
6
+ accounts: {
7
+ oauth2: {
8
+ initCodeClient(config: {
9
+ client_id: string;
10
+ scope: string;
11
+ ux_mode: "popup" | "redirect";
12
+ callback: (response: {
13
+ code?: string;
14
+ error?: string;
15
+ }) => void;
16
+ }): {
17
+ requestCode(): void;
18
+ };
19
+ };
20
+ };
21
+ };
22
+ }
23
+ }
2
24
  import { AuthControllerExtended } from "@rebasepro/types";
3
25
  /**
4
26
  * Props for the generic LoginView.
@@ -14,3 +14,4 @@ export * from "./UserSelectPopover";
14
14
  export * from "./UserDisplay";
15
15
  export * from "./LoginView";
16
16
  export * from "./RebaseAuth";
17
+ export * from "./BootstrapAdminBanner";
@@ -35,3 +35,4 @@ export * from "./useTranslation";
35
35
  export * from "./useRebaseClient";
36
36
  export * from "./useAnalyticsController";
37
37
  export * from "./useUserConfigurationPersistence";
38
+ export * from "./useResolvedComponent";
@@ -3,10 +3,25 @@
3
3
  * with localStorage persistence. Automatically cleans up stale group entries
4
4
  * when groups are removed from the navigation.
5
5
  *
6
+ * Groups that have never been toggled by the user fall back to
7
+ * `defaults[groupName]` (driven by `collapsedByDefault` in config).
8
+ *
6
9
  * @param groupNames - Array of group names to track
7
10
  * @param namespace - Namespace for localStorage key (e.g., "home", "drawer") to allow independent state
11
+ * @param defaults - Optional map of group name → collapsed boolean from config
8
12
  */
9
- export declare function useCollapsedGroups(groupNames: string[], namespace?: string): {
13
+ export declare function useCollapsedGroups(groupNames: string[], namespace?: string, defaults?: Record<string, boolean>): {
10
14
  isGroupCollapsed: (name: string) => boolean;
11
15
  toggleGroupCollapsed: (name: string) => void;
12
16
  };
17
+ /**
18
+ * Build a defaults map from navigationGroupMappings for a given namespace.
19
+ * Returns a Record<groupName, collapsed> that can be passed to useCollapsedGroups.
20
+ */
21
+ export declare function buildCollapsedDefaults(mappings: Array<{
22
+ name: string;
23
+ collapsedByDefault?: boolean | {
24
+ drawer?: boolean;
25
+ home?: boolean;
26
+ };
27
+ }> | undefined, namespace: "drawer" | "home"): Record<string, boolean>;
@@ -0,0 +1,47 @@
1
+ import React from "react";
2
+ import type { ComponentRef } from "@rebasepro/types";
3
+ /**
4
+ * Resolves a `ComponentRef` into a renderable `React.ComponentType`.
5
+ *
6
+ * This hook handles all three forms of `ComponentRef`:
7
+ *
8
+ * 1. **`LazyComponentRef`** (produced by the Vite plugin from string paths):
9
+ * Wraps the lazy loader with `React.lazy()` for automatic code-splitting.
10
+ *
11
+ * 2. **`() => Promise<{ default: ComponentType }>`** (manual lazy import):
12
+ * Also wraps with `React.lazy()`. Distinguished from regular components
13
+ * by checking that the function has no parameters and is not a known
14
+ * React internal (no `$$typeof`).
15
+ *
16
+ * 3. **Direct `React.ComponentType`**:
17
+ * Returned as-is.
18
+ *
19
+ * If the ref is `undefined` or a raw string (which should never happen at
20
+ * runtime in the browser since the Vite plugin transforms strings), returns `undefined`.
21
+ *
22
+ * The returned component is stable across re-renders as long as the `ref`
23
+ * is referentially stable.
24
+ *
25
+ * **Usage:** Wrap the rendered component in `<Suspense>` to handle the
26
+ * loading state of lazy components.
27
+ *
28
+ * @example
29
+ * ```tsx
30
+ * const ResolvedField = useResolvedComponent(property.ui?.Field);
31
+ * if (!ResolvedField) return null;
32
+ * return (
33
+ * <Suspense fallback={<CircularProgress />}>
34
+ * <ResolvedField {...fieldProps} />
35
+ * </Suspense>
36
+ * );
37
+ * ```
38
+ */
39
+ export declare function useResolvedComponent<P = unknown>(ref: ComponentRef<P> | undefined): React.ComponentType<P> | undefined;
40
+ /**
41
+ * Pure function version of the resolver, for use outside React components.
42
+ * Same resolution logic as `useResolvedComponent`.
43
+ *
44
+ * Results are cached per reference identity — calling this multiple times
45
+ * with the same `ref` object returns the same `React.ComponentType`.
46
+ */
47
+ export declare function resolveComponentRef<P = unknown>(ref: ComponentRef<P> | undefined): React.ComponentType<P> | undefined;
package/dist/index.es.js CHANGED
@@ -1,9 +1,9 @@
1
1
  import { jsx, jsxs, Fragment } from "react/jsx-runtime";
2
2
  import { c } from "react-compiler-runtime";
3
- import React, { useRef, useEffect, useState, useContext, useCallback, useMemo, createContext, useLayoutEffect } from "react";
3
+ import React, { useRef, useEffect, useState, useContext, useCallback, useMemo, createContext, lazy, useLayoutEffect } from "react";
4
4
  import { ErrorBoundary, Tooltip, Typography, Button, DialogTitle, DialogContent, LoadingButton, DialogActions, Dialog, Paper, IconButton, Container, cls, defaultBorderMixin, iconSize, MenuItem, Menu, Table, TableBody, Chip, CircularProgress, TableRow, TableCell, Checkbox, Avatar, Tabs, Tab, TextField, Select, SelectItem, MultiSelect, MultiSelectItem, BooleanSwitch, SearchBar, Skeleton, Alert, Separator, TableHeader, Popover, CenteredView, iconKeys, coolIconKeys, colorClassesMapping, getColorSchemeForSeed, CHIP_COLORS } from "@rebasepro/ui";
5
5
  import { SnackbarProvider as SnackbarProvider$1, useSnackbar } from "notistack";
6
- import { EntityRelation, EntityReference, Vector, GeoPoint } from "@rebasepro/types";
6
+ import { EntityRelation, isLazyComponentRef, EntityReference, Vector, GeoPoint } from "@rebasepro/types";
7
7
  import { useBlocker, useLocation, Link, createBrowserRouter, RouterProvider, Routes } from "react-router-dom";
8
8
  import { stripCollectionPath, canCreateEntity, canEditEntity, canDeleteEntity, canReadCollection, getSubcollections, buildRebaseData, removeInitialAndTrailingSlashes, resolveStorageFilenameString, resolveStoragePathString, isPropertyBuilder, getLabelOrConfigFrom } from "@rebasepro/common";
9
9
  import { mergeDeep, slugify, hashString, isObject, isPlainObject, randomString } from "@rebasepro/utils";
@@ -378,6 +378,9 @@ function _temp$7(e) {
378
378
  const useAuthController = () => {
379
379
  return useContext(AuthControllerContext);
380
380
  };
381
+ function useRebaseClient() {
382
+ return useContext(RebaseClientInstanceContext);
383
+ }
381
384
  const useStorageSource = (collection) => {
382
385
  const defaultStorageSource = useContext(StorageSourceContext);
383
386
  if (collection?.overrides?.storageSource) {
@@ -433,6 +436,7 @@ function useInternalUserManagementController() {
433
436
  }
434
437
  const DatabaseAdminContext = React.createContext(void 0);
435
438
  const useRebaseContext = () => {
439
+ const client = useRebaseClient();
436
440
  const authController = useAuthController();
437
441
  const data = useData();
438
442
  const storageSource = useStorageSource();
@@ -455,7 +459,9 @@ const useRebaseContext = () => {
455
459
  analyticsController,
456
460
  userManagement,
457
461
  effectiveRoleController,
458
- databaseAdmin
462
+ databaseAdmin,
463
+ client
464
+ // Client should be provided
459
465
  });
460
466
  React.useEffect(() => {
461
467
  rebaseContextRef.current = {
@@ -469,9 +475,10 @@ const useRebaseContext = () => {
469
475
  analyticsController,
470
476
  userManagement,
471
477
  effectiveRoleController,
472
- databaseAdmin
478
+ databaseAdmin,
479
+ client
473
480
  };
474
- }, [authController, dialogsController, effectiveRoleController, data, databaseAdmin]);
481
+ }, [authController, data, storageSource, snackbarController, userConfigPersistence, dialogsController, customizationController, analyticsController, userManagement, effectiveRoleController, databaseAdmin, client]);
475
482
  return rebaseContextRef.current;
476
483
  };
477
484
  const CACHE = {};
@@ -1370,7 +1377,7 @@ function checkLargeLayout(breakpoint = "lg") {
1370
1377
  return window.matchMedia(`(min-width: ${breakpoints[breakpoint] + 1}px)`).matches;
1371
1378
  }
1372
1379
  const STORAGE_KEY_PREFIX = "rebase-collapsed-groups";
1373
- function useCollapsedGroups(groupNames, namespace = "default") {
1380
+ function useCollapsedGroups(groupNames, namespace = "default", defaults) {
1374
1381
  const storageKey = `${STORAGE_KEY_PREFIX}-${namespace}`;
1375
1382
  const [collapsedGroups, setCollapsedGroups] = useState(() => {
1376
1383
  try {
@@ -1400,19 +1407,42 @@ function useCollapsedGroups(groupNames, namespace = "default") {
1400
1407
  });
1401
1408
  }, [groupNames]);
1402
1409
  const isGroupCollapsed = useCallback((name) => {
1403
- return !!collapsedGroups[name];
1404
- }, [collapsedGroups]);
1410
+ if (name in collapsedGroups) {
1411
+ return collapsedGroups[name];
1412
+ }
1413
+ return defaults?.[name] ?? false;
1414
+ }, [collapsedGroups, defaults]);
1405
1415
  const toggleGroupCollapsed = useCallback((name_0) => {
1406
- setCollapsedGroups((prev_0) => ({
1407
- ...prev_0,
1408
- [name_0]: !prev_0[name_0]
1409
- }));
1410
- }, []);
1416
+ setCollapsedGroups((prev_0) => {
1417
+ const currentlyCollapsed = name_0 in prev_0 ? prev_0[name_0] : defaults?.[name_0] ?? false;
1418
+ return {
1419
+ ...prev_0,
1420
+ [name_0]: !currentlyCollapsed
1421
+ };
1422
+ });
1423
+ }, [defaults]);
1411
1424
  return useMemo(() => ({
1412
1425
  isGroupCollapsed,
1413
1426
  toggleGroupCollapsed
1414
1427
  }), [isGroupCollapsed, toggleGroupCollapsed]);
1415
1428
  }
1429
+ function buildCollapsedDefaults(mappings, namespace) {
1430
+ if (!mappings) return {};
1431
+ const result = {};
1432
+ for (const mapping of mappings) {
1433
+ const val = mapping.collapsedByDefault;
1434
+ if (val === void 0) continue;
1435
+ if (typeof val === "boolean") {
1436
+ result[mapping.name] = val;
1437
+ } else {
1438
+ const ns = val[namespace];
1439
+ if (ns !== void 0) {
1440
+ result[mapping.name] = ns;
1441
+ }
1442
+ }
1443
+ }
1444
+ return result;
1445
+ }
1416
1446
  const rebaseLogo = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAa9SURBVHgB7Z1NbBNHFMf/a7uBEgMOHy2pSlnUql+oEG7lgNgcqZAgR9RKSU5tT9mcqp6SXNoj5tBzHAmk3ggSUo/Ziko9skXqoVIlJm3Von4IhzhAIIk7b9ab+COJvZ63610nP8netWx5Z//73syb2Zm3BjqJXc4BixaQMgHjDFCWW5iVb83aH5eL8jdC7lS2a/Ny3wGyLvJGER3CQJRsCHZBfrqCBpHaRkCJuXYb2O9EKWg0AtpKtDF5gnJr5BA+BSVm/sAsQiY8AZW1PSHR7IhE2wwhX1NAWlrlywIhwC9gPISrR8iyFJDvnQIzvALapRH5PgG+uo0bAbLIfLYAJngEtJ+awOq03LOQCAxZN6bGOdw6BV3GyV1X7iEx4hFlGQGs3qt4jBbtW6Cq65bIXW0km7x06XG0SXsCei57S+4NoDtwZUs91I5LBxfQE28O8W0o2kVIEQeDihhMwO4Vz0cEFbF1AbtfPB8RRMTWBNw54vmIVkVsMYxRDYaJnYOpztl+1LQn1VxAu3QN3dPaBkGe80sTzX60vQt7geY0IsDcMw8r+wPO9N6H2TOPXHpBbn9DcfWgehHu09OYf/YGnNJ5uE9OIyJkjyWb3+rLrQVU9R71MMIbELD238XlvjsYOXQTuUywIbziSg6zC5cw8+/HcBbPI0RkwdJnt6oPtxFw6ZbX5eFn5PANDB+5qQTkQCyfwNSfX6Lw3ycICTkclh3c7IvNBQzJdUmwide+YhOunpCFHN1sFGcrAR+AsdUl95zo/xr2q98gCkhAEpIEZUQAL6Qr99XUNY2tsL3EOp5HjcPc2x9FJh5BVQQdk47NiAlkGgZOai2QOWD2xWM+kZah1nvwl+84W2xpfS9OVlthnQWuWugS8QgKhebeuYiBfffBRK7eCutduGng2ApxEM+HX0RjrPrThoD2Y7b7tHERz4dEvPXmVbXl+DvvNq1HlQWmLoMBClPiJJ4PlWn65KfgIbVuhV4jojrNLz2CJlTIBx+cQpwZ+vVbzBYvQZP1xqRigWkLDEybnyHuXDv+BYcrS4PrsWinIqC++17J3Qmth8EJeQlPTErTVDbqQAuajEUYKOsyfPgm9DGU0Rkc9V8S6r56KMDWH8V50SctMKM9WErumzSGj9yAPj0WubAFTXhcIlp4LnrZTFVmhbYNtWiMXaXI4Cm3cUYKmNIa80mieD76UYOyQGgN2SdZQLrnovsXJKAJDRgK0TE4upzaFpjLsHTQO8JB/R6JmYEuyz3A4gEkkuW90EVfwLvyZtXffUgkB03gfWihP0N1h0MCCmhQTOu7QadY0C97UdsCxd64rGQIjtijXXahbYHuvn4kFbf3GDQhC1zTCobcfdqF6Bhur+7FN8gCUy40KGb2wjlgImmQ++pf/LV5skABTb6ncCBhMF10Rwq46kCT/LFzSBozr5yFPituqjJNQUCDpLkxuS9DeUXVXbnybWgydXwQSWHquAUGHHrzBXSgCV3RJFghWV/hKIf7GsroKgKqelB7mfzoW0OIO0zWJ3nu0LsnoKoH9a2Qru64eRFxpXB0gMn6UPCnuFV35a6DgXz/h7F0Zd6La6y3GXUTLEt0f1i7g2guFzH387TaxgESb/DUKEffV/0d8tmT/oe6wYQyixUyF1gLCrGG3r3KWZaavAt1Aq7QghIWs4mDiCQelYGxvy5UBpAqagX0GhMWK1RHq4jIMOrR1rHPnv6cebCjPFO/4KZxmYM3V4ZyIJhgZPIPBxO/zyEKrvefw+TrlrJARmrqPp9IF9qE3biQ1VEsGlIUEGChDWGXyFwshMDIPy7G/voRA0sPwQFVEddl+MQU422CMYt876a9hCaLDVfJlUNrBQaePIQthbywIAJbJbnnjAyMZw+9F3LcSVnjMm0sNiTsEq2JuIYIIDHNZ0VYjx/gxPMicivPar4nweZ7cmoUmSwuwpHw0e0yHTVf8m8v5uvXRuwcZESS379tXpwW7sqtTELlVdlxuM3EI5oLqGLDNFWgAjsHUTnnpuymPWlE8Kc98dlNvNNAMAGJ3dRPNQSf2qEOkKYbIN3UsLjtiEcEt8BquiLEocGT7GS7mX/1BCS8fjMF2wmbZaTyUk9tlxOmFfQFJLwcM1JE4wqSgSNddpQjBSiPgD67SWiZsBcn5V8PIzZCkrvSTTPprsxZzsMRkPDCHQsdtcjwhPMJT8BqvHwM8pUaRuiQaCkHWKOBAAchE42APusPI1Bi0gMJTPAgvPk9NDmgGx9GsBVK0NIA1Mg3rdlTCx9z3rYha5zY2NLjMMo/eXMboxWsnv8Br15XnnLWoGsAAAAASUVORK5CYII=";
1417
1447
  function useBrowserTitleAndIcon(name, logo) {
1418
1448
  const $ = c(4);
@@ -2166,8 +2196,58 @@ function useTranslation() {
2166
2196
  t1 = t2;
2167
2197
  return t1;
2168
2198
  }
2169
- function useRebaseClient() {
2170
- return useContext(RebaseClientInstanceContext);
2199
+ const lazyCache = /* @__PURE__ */ new WeakMap();
2200
+ function useResolvedComponent(ref) {
2201
+ const $ = c(2);
2202
+ let t0;
2203
+ let t1;
2204
+ if ($[0] !== ref) {
2205
+ t1 = resolveComponentRef(ref);
2206
+ $[0] = ref;
2207
+ $[1] = t1;
2208
+ } else {
2209
+ t1 = $[1];
2210
+ }
2211
+ t0 = t1;
2212
+ return t0;
2213
+ }
2214
+ function getOrCreateLazy(key, loader) {
2215
+ const cached = lazyCache.get(key);
2216
+ if (cached) return cached;
2217
+ const LazyComponent = lazy(loader);
2218
+ lazyCache.set(key, LazyComponent);
2219
+ return LazyComponent;
2220
+ }
2221
+ function resolveComponentRef(ref) {
2222
+ if (ref == null) return void 0;
2223
+ if (typeof ref === "string") {
2224
+ console.warn(`[Rebase] Encountered a raw string ComponentRef ("${ref}") at runtime. This usually means the Vite transform plugin did not process this file. Ensure the file is inside the configured collectionsDir.`);
2225
+ return void 0;
2226
+ }
2227
+ if (isLazyComponentRef(ref)) {
2228
+ return getOrCreateLazy(ref, () => ref.load());
2229
+ }
2230
+ if (typeof ref === "function") {
2231
+ const fn = ref;
2232
+ if (fn.prototype?.isReactComponent) {
2233
+ return ref;
2234
+ }
2235
+ if ("$$typeof" in fn) {
2236
+ return ref;
2237
+ }
2238
+ if (fn.length > 0) {
2239
+ return ref;
2240
+ }
2241
+ const name = fn.name;
2242
+ if (name && /^[A-Z]/.test(name)) {
2243
+ return ref;
2244
+ }
2245
+ return getOrCreateLazy(fn, ref);
2246
+ }
2247
+ if (typeof ref === "object" && "$$typeof" in ref) {
2248
+ return ref;
2249
+ }
2250
+ return void 0;
2171
2251
  }
2172
2252
  function ErrorTooltip(props) {
2173
2253
  const $ = c(2);
@@ -2481,7 +2561,15 @@ function useDataTableController({
2481
2561
  const fixedFilter = fixedFilterFromProps ?? fixedFilterFromCollection;
2482
2562
  const paginationEnabled = collection.pagination === void 0 || Boolean(collection.pagination);
2483
2563
  const pageSize = typeof collection.pagination === "number" ? collection.pagination : DEFAULT_PAGE_SIZE;
2484
- const [searchString, setSearchString] = React.useState();
2564
+ const location = useLocation();
2565
+ const [searchString, setSearchString] = React.useState(() => {
2566
+ if (updateUrl) {
2567
+ const params = new URLSearchParams(location.search);
2568
+ const urlSearch = params.get("search");
2569
+ return urlSearch ? decodeURIComponent(urlSearch) : void 0;
2570
+ }
2571
+ return void 0;
2572
+ });
2485
2573
  const checkFilterCombination = useCallback((filterValues, sortBy) => {
2486
2574
  return true;
2487
2575
  }, []);
@@ -2492,7 +2580,6 @@ function useDataTableController({
2492
2580
  }
2493
2581
  return sort;
2494
2582
  }, [sort, fixedFilter]);
2495
- const location = useLocation();
2496
2583
  const {
2497
2584
  filterValues: filterUrl,
2498
2585
  sortBy: sortUrl
@@ -2519,6 +2606,9 @@ function useDataTableController({
2519
2606
  } else {
2520
2607
  setSortBy(urlSortBy);
2521
2608
  }
2609
+ const urlParams = new URLSearchParams(location.search);
2610
+ const urlSearch_0 = urlParams.get("search");
2611
+ setSearchString(urlSearch_0 ? decodeURIComponent(urlSearch_0) : void 0);
2522
2612
  }, [location.search, updateUrl, fixedFilter, checkFilterCombination]);
2523
2613
  useUpdateUrl(filterValues_0, sortBy_0, searchString, updateUrl);
2524
2614
  const collectionScroll = scrollRestoration?.getCollectionScroll(path, filterValues_0);
@@ -6329,7 +6419,7 @@ function LoginView(t0) {
6329
6419
  t4 = $[2];
6330
6420
  }
6331
6421
  const caps = t4;
6332
- const isBootstrapMode = needsSetup ?? authController.needsSetup ?? false;
6422
+ const isBootstrapMode = needsSetup ?? ("needsSetup" in authController && !!authController.needsSetup) ?? false;
6333
6423
  const canRegister = registrationEnabled ?? caps.registration ?? false;
6334
6424
  const hasGoogleLogin = googleEnabled ?? caps.googleLogin ?? false;
6335
6425
  const hasPasswordReset = caps.passwordReset ?? !!authController.forgotPassword;
@@ -6612,7 +6702,11 @@ function LoginView(t0) {
6612
6702
  mode === "buttons" && /* @__PURE__ */ jsxs("div", { className: "w-full flex flex-col gap-3 mt-2", children: [
6613
6703
  /* @__PURE__ */ jsx(LoginButton, { disabled, text: "Sign in with email", icon: /* @__PURE__ */ jsx(MailIcon, {}), onClick: () => switchMode("login") }),
6614
6704
  hasGoogleLogin && googleClientId && /* @__PURE__ */ jsx(GoogleLoginButton, { disabled, googleClientId, authController }),
6615
- showRegistration && /* @__PURE__ */ jsx(Button, { className: "w-full", variant: "filled", color: "primary", size: "large", onClick: () => switchMode("register"), children: "Create an account" })
6705
+ showRegistration && /* @__PURE__ */ jsx("div", { className: "mt-2 text-center", children: /* @__PURE__ */ jsxs(Typography, { variant: "body2", color: "secondary", children: [
6706
+ "Don't have an account?",
6707
+ " ",
6708
+ /* @__PURE__ */ jsx("button", { type: "button", className: "font-semibold hover:underline cursor-pointer text-primary-600 dark:text-primary-400", onClick: () => switchMode("register"), children: "Create one" })
6709
+ ] }) })
6616
6710
  ] }),
6617
6711
  mode === "login" && /* @__PURE__ */ jsx(LoginForm, { authController, registrationMode: false, onClose: () => switchMode("buttons"), onForgotPassword: hasPasswordReset ? () => switchMode("forgot") : void 0, noUserComponent, disableSignupScreen, switchToRegister: showRegistration ? () => switchMode("register") : void 0 }),
6618
6712
  mode === "register" && /* @__PURE__ */ jsx(LoginForm, { authController, registrationMode: true, onClose: () => switchMode("buttons"), onForgotPassword: hasPasswordReset ? () => switchMode("forgot") : void 0, noUserComponent, disableSignupScreen, switchToLogin: () => switchMode("login") }),
@@ -6751,7 +6845,7 @@ function GoogleLoginButton(t0) {
6751
6845
  googleClientId,
6752
6846
  authController
6753
6847
  } = t0;
6754
- const tokenClientRef = useRef(null);
6848
+ const codeClientRef = useRef(null);
6755
6849
  let t1;
6756
6850
  if ($[0] !== authController.googleLogin || $[1] !== googleClientId) {
6757
6851
  t1 = () => {
@@ -6759,19 +6853,23 @@ function GoogleLoginButton(t0) {
6759
6853
  return;
6760
6854
  }
6761
6855
  const google = window.google;
6762
- if (!google || tokenClientRef.current) {
6856
+ if (!google || codeClientRef.current) {
6763
6857
  return;
6764
6858
  }
6765
- tokenClientRef.current = google.accounts.oauth2.initTokenClient({
6859
+ codeClientRef.current = google.accounts.oauth2.initCodeClient({
6766
6860
  client_id: googleClientId,
6767
6861
  scope: "openid email profile",
6862
+ ux_mode: "popup",
6768
6863
  callback: async (response) => {
6769
- if (response.error || !response.access_token) {
6864
+ if (response.error || !response.code) {
6770
6865
  console.error("Google login error:", response.error);
6771
6866
  return;
6772
6867
  }
6773
6868
  try {
6774
- await authController.googleLogin(response.access_token, "accessToken");
6869
+ await authController.googleLogin({
6870
+ code: response.code,
6871
+ redirectUri: "postmessage"
6872
+ });
6775
6873
  } catch (t22) {
6776
6874
  const err = t22;
6777
6875
  console.error("Google login error:", err);
@@ -6798,11 +6896,11 @@ function GoogleLoginButton(t0) {
6798
6896
  let t3;
6799
6897
  if ($[6] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) {
6800
6898
  t3 = () => {
6801
- if (!tokenClientRef.current) {
6899
+ if (!codeClientRef.current) {
6802
6900
  console.error("Google Sign-In not loaded");
6803
6901
  return;
6804
6902
  }
6805
- tokenClientRef.current.requestAccessToken();
6903
+ codeClientRef.current.requestCode();
6806
6904
  };
6807
6905
  $[6] = t3;
6808
6906
  } else {
@@ -7365,6 +7463,58 @@ function RebaseAuth(t0) {
7365
7463
  useLayoutEffect(t1, t2);
7366
7464
  return null;
7367
7465
  }
7466
+ function BootstrapAdminBanner({
7467
+ className
7468
+ }) {
7469
+ const userManagement = useInternalUserManagementController();
7470
+ const {
7471
+ user: loggedInUser
7472
+ } = useAuthController();
7473
+ const {
7474
+ t
7475
+ } = useTranslation();
7476
+ const snackbarController = useSnackbarController();
7477
+ const [bootstrapping, setBootstrapping] = useState(false);
7478
+ if (typeof window !== "undefined" && window.location.hostname !== "localhost" && window.location.hostname !== "127.0.0.1") {
7479
+ return null;
7480
+ }
7481
+ if (!userManagement || !loggedInUser) {
7482
+ return null;
7483
+ }
7484
+ const {
7485
+ users,
7486
+ loading: delegateLoading,
7487
+ bootstrapAdmin,
7488
+ usersError
7489
+ } = userManagement;
7490
+ const hasAdmin = users.some((u) => u.roles?.includes("admin"));
7491
+ if (delegateLoading || hasAdmin || usersError || !bootstrapAdmin) {
7492
+ return null;
7493
+ }
7494
+ const handleBootstrap = async () => {
7495
+ if (!bootstrapAdmin) return;
7496
+ setBootstrapping(true);
7497
+ try {
7498
+ await bootstrapAdmin();
7499
+ snackbarController.open({
7500
+ type: "success",
7501
+ message: t("bootstrap_admin_success") || "Admin successfully created"
7502
+ });
7503
+ window.location.reload();
7504
+ } catch (error) {
7505
+ snackbarController.open({
7506
+ type: "error",
7507
+ message: error instanceof Error ? error.message : t("failed_to_bootstrap_admin") || "Failed to bootstrap admin"
7508
+ });
7509
+ } finally {
7510
+ setBootstrapping(false);
7511
+ }
7512
+ };
7513
+ return /* @__PURE__ */ jsxs("div", { className: `bg-yellow-100 dark:bg-yellow-900 border border-yellow-400 dark:border-yellow-700 rounded p-4 flex items-center justify-between ${className || ""}`, children: [
7514
+ /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(Typography, { variant: "label", className: "text-yellow-800 dark:text-yellow-200", children: t("no_users_or_roles_defined") || "No admins found. Click to add your user as admin." }) }),
7515
+ /* @__PURE__ */ jsx(Button, { onClick: handleBootstrap, disabled: bootstrapping, children: bootstrapping ? /* @__PURE__ */ jsx(CircularProgress, { size: "small" }) : t("add_logged_user_as_admin") || "Add logged user as admin" })
7516
+ ] });
7517
+ }
7368
7518
  const en = {
7369
7519
  // ─── Form actions ────────────────────────────────────────────
7370
7520
  save: "Save",
@@ -7405,6 +7555,7 @@ const en = {
7405
7555
  all_entries_loaded: "All {{count}} entries loaded",
7406
7556
  create_your_first_entry: "Create your first entry",
7407
7557
  no_results_filter_sort: "No results with the applied filter/sort",
7558
+ no_results_search: 'No results found for "{{search}}"',
7408
7559
  add: "Add",
7409
7560
  remove: "Remove",
7410
7561
  multiple_entities: "Multiple entities",
@@ -8277,6 +8428,7 @@ const es = {
8277
8428
  all_entries_loaded: "Todas las {{count}} entradas cargadas",
8278
8429
  create_your_first_entry: "Crea tu primera entrada",
8279
8430
  no_results_filter_sort: "No hay resultados con el filtro/orden aplicado",
8431
+ no_results_search: 'No se encontraron resultados para "{{search}}"',
8280
8432
  add: "Añadir",
8281
8433
  remove: "Quitar",
8282
8434
  multiple_entities: "Múltiples entidades",
@@ -9122,6 +9274,7 @@ const de = {
9122
9274
  all_entries_loaded: "Alle {{count}} Einträge geladen",
9123
9275
  create_your_first_entry: "Erstellen Sie Ihren ersten Eintrag",
9124
9276
  no_results_filter_sort: "Keine Ergebnisse mit angewendetem Filter/Sortierung",
9277
+ no_results_search: 'Keine Ergebnisse gefunden für "{{search}}"',
9125
9278
  add: "Hinzufügen",
9126
9279
  remove: "Entfernen",
9127
9280
  multiple_entities: "Mehrere Entitäten",
@@ -9966,6 +10119,7 @@ const fr = {
9966
10119
  all_entries_loaded: "Toutes les {{count}} entrées chargées",
9967
10120
  create_your_first_entry: "Créez votre première entrée",
9968
10121
  no_results_filter_sort: "Aucun résultat avec le filtre/tri appliqué",
10122
+ no_results_search: 'Aucun résultat trouvé pour "{{search}}"',
9969
10123
  add: "Ajouter",
9970
10124
  remove: "Supprimer",
9971
10125
  multiple_entities: "Entités multiples",
@@ -10810,6 +10964,7 @@ const it = {
10810
10964
  all_entries_loaded: "Tutte le {{count}} voci caricate",
10811
10965
  create_your_first_entry: "Crea la tua prima voce",
10812
10966
  no_results_filter_sort: "Nessun risultato con il filtro/ordinamento applicato",
10967
+ no_results_search: 'Nessun risultato trovato per "{{search}}"',
10813
10968
  add: "Aggiungi",
10814
10969
  remove: "Rimuovi",
10815
10970
  multiple_entities: "Entità multiple",
@@ -11654,6 +11809,7 @@ const hi = {
11654
11809
  all_entries_loaded: "सभी {{count}} प्रविष्टियाँ लोड हो गईं",
11655
11810
  create_your_first_entry: "अपनी पहली प्रविष्टि बनाएं",
11656
11811
  no_results_filter_sort: "लागू किए गए फ़िल्टर/सॉर्ट के साथ कोई परिणाम नहीं",
11812
+ no_results_search: '"{{search}}" के लिए कोई परिणाम नहीं मिला',
11657
11813
  add: "जोड़ें",
11658
11814
  remove: "हटाएं",
11659
11815
  multiple_entities: "एकाधिक इकाइयां",
@@ -12498,6 +12654,7 @@ const pt = {
12498
12654
  all_entries_loaded: "Todos os {{count}} registos carregados",
12499
12655
  create_your_first_entry: "Crie o seu primeiro registo",
12500
12656
  no_results_filter_sort: "Sem resultados com o filtro/ordenação aplicado",
12657
+ no_results_search: 'Nenhum resultado encontrado para "{{search}}"',
12501
12658
  add: "Adicionar",
12502
12659
  remove: "Remover",
12503
12660
  multiple_entities: "Múltiplas entidades",
@@ -13474,18 +13631,19 @@ function Rebase(props) {
13474
13631
  }
13475
13632
  const ws = client?.ws;
13476
13633
  if (ws && typeof ws.executeSql === "function") {
13634
+ const wsAdmin = ws;
13477
13635
  return {
13478
- executeSql: ws.executeSql.bind(ws),
13479
- fetchAvailableDatabases: ws.fetchAvailableDatabases?.bind(ws),
13480
- fetchAvailableRoles: ws.fetchAvailableRoles?.bind(ws),
13481
- fetchCurrentDatabase: ws.fetchCurrentDatabase?.bind(ws),
13482
- fetchUnmappedTables: ws.fetchUnmappedTables?.bind(ws),
13483
- fetchTableMetadata: ws.fetchTableMetadata?.bind(ws),
13636
+ executeSql: wsAdmin.executeSql.bind(wsAdmin),
13637
+ fetchAvailableDatabases: wsAdmin.fetchAvailableDatabases?.bind(wsAdmin),
13638
+ fetchAvailableRoles: wsAdmin.fetchAvailableRoles?.bind(wsAdmin),
13639
+ fetchCurrentDatabase: wsAdmin.fetchCurrentDatabase?.bind(wsAdmin),
13640
+ fetchUnmappedTables: wsAdmin.fetchUnmappedTables?.bind(wsAdmin),
13641
+ fetchTableMetadata: wsAdmin.fetchTableMetadata?.bind(wsAdmin),
13484
13642
  // Branch admin capabilities
13485
- ...typeof ws.createBranch === "function" ? {
13486
- createBranch: ws.createBranch.bind(ws),
13487
- deleteBranch: ws.deleteBranch.bind(ws),
13488
- listBranches: ws.listBranches.bind(ws)
13643
+ ...typeof wsAdmin.createBranch === "function" ? {
13644
+ createBranch: wsAdmin.createBranch.bind(wsAdmin),
13645
+ deleteBranch: wsAdmin.deleteBranch.bind(wsAdmin),
13646
+ listBranches: wsAdmin.listBranches.bind(wsAdmin)
13489
13647
  } : {}
13490
13648
  };
13491
13649
  }
@@ -13547,7 +13705,9 @@ function RebaseInternal(t0) {
13547
13705
  }
13548
13706
  const childrenResult = t1;
13549
13707
  const plugins = customizationController.plugins;
13550
- if (!loading && plugins && plugins.length > 0) {
13708
+ const authController = context.authController;
13709
+ const authReady = !loading && !authController.authLoading && (Boolean(authController.user) || authController.loginSkipped);
13710
+ if (authReady && plugins && plugins.length > 0) {
13551
13711
  let t2;
13552
13712
  if ($[4] !== context) {
13553
13713
  t2 = {
@@ -14299,7 +14459,7 @@ function getEntityPreviewKeys(authController, targetCollection, fields, previewP
14299
14459
  if (listProperties && listProperties.length > 0) {
14300
14460
  return listProperties;
14301
14461
  } else {
14302
- listProperties = allProperties;
14462
+ listProperties = targetCollection.propertiesOrder || allProperties;
14303
14463
  return listProperties.filter((key) => {
14304
14464
  const prop = targetCollection.properties[key];
14305
14465
  const isIdProp = prop && typeof prop === "object" && "isId" in prop && Boolean(prop.isId);
@@ -14314,16 +14474,24 @@ function getEntityTitlePropertyKey(collection, propertyConfigs) {
14314
14474
  if (collection.titleProperty) {
14315
14475
  return collection.titleProperty;
14316
14476
  }
14317
- for (const key in collection.properties) {
14477
+ const orderToSearch = collection.propertiesOrder || Object.keys(collection.properties);
14478
+ let firstStringCandidate;
14479
+ for (const key of orderToSearch) {
14318
14480
  const property = collection.properties[key];
14319
- if (!isPropertyBuilder(property)) {
14481
+ if (property && !isPropertyBuilder(property)) {
14320
14482
  const prop = property;
14321
14483
  if (prop.type === "string" && !prop.ui?.multiline && !prop.ui?.markdown && !prop.storage && !prop.isId) {
14322
- return key;
14484
+ if (!firstStringCandidate) {
14485
+ firstStringCandidate = key;
14486
+ }
14487
+ const lowerKey = key.toLowerCase();
14488
+ if (["name", "title", "label", "displayname", "username"].includes(lowerKey)) {
14489
+ return key;
14490
+ }
14323
14491
  }
14324
14492
  }
14325
14493
  }
14326
- return void 0;
14494
+ return firstStringCandidate;
14327
14495
  }
14328
14496
  function getColorScheme(enumValues, key) {
14329
14497
  const labelOrConfig = getLabelOrConfigFrom(enumValues, key);
@@ -14665,6 +14833,7 @@ export {
14665
14833
  AnalyticsContext,
14666
14834
  ApiConfigProvider,
14667
14835
  AuthControllerContext,
14836
+ BootstrapAdminBanner,
14668
14837
  CONTAINER_FULL_WIDTH,
14669
14838
  ConfirmationDialog,
14670
14839
  CustomizationControllerContext,
@@ -14708,6 +14877,7 @@ export {
14708
14877
  UserDisplay,
14709
14878
  UserSelectPopover,
14710
14879
  UserSettingsView,
14880
+ buildCollapsedDefaults,
14711
14881
  buildEnumLabel,
14712
14882
  clearEntityCache,
14713
14883
  createFormexStub,
@@ -14733,6 +14903,7 @@ export {
14733
14903
  printChanged,
14734
14904
  removeEntityFromCache,
14735
14905
  removeEntityFromMemoryCache,
14906
+ resolveComponentRef,
14736
14907
  saveEntityToCache,
14737
14908
  saveEntityToMemoryCache,
14738
14909
  saveEntityWithCallbacks,
@@ -14768,6 +14939,7 @@ export {
14768
14939
  useRebaseRegistry,
14769
14940
  useRebaseRegistryDispatch,
14770
14941
  useRelationSelector,
14942
+ useResolvedComponent,
14771
14943
  useRestoreScroll,
14772
14944
  useScrollRestoration,
14773
14945
  useSlot,