@rebasepro/app 0.11.1-canary.gfadf355 → 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.
@@ -104,10 +104,18 @@ export interface LoginViewProps {
104
104
  * Pre-fill the password field (e.g. for demo or testing environments).
105
105
  */
106
106
  defaultPassword?: string;
107
+ /**
108
+ * When set, the email/password forms render a "Join the newsletter"
109
+ * opt-in checkbox. Called with the address that just authenticated —
110
+ * sign-in or registration — if the box was ticked. Wire it to whatever
111
+ * stores the subscription; failures are the callback's problem, the
112
+ * login flow never waits on it.
113
+ */
114
+ onNewsletterOptIn?: (email: string) => void;
107
115
  }
108
116
  /**
109
117
  * Generic login view component that works with any AuthControllerExtended.
110
118
  * Feature-detects capabilities to show/hide login methods.
111
119
  * @group Core
112
120
  */
113
- export declare function LoginView({ logo, authController, noUserComponent, disableSignupScreen, disabled, notAllowedError, googleClientId, githubClientId, linkedinClientId, title, subtitle, needsSetup, registrationEnabled, additionalComponent, topComponent, defaultEmail, defaultPassword }: LoginViewProps): React.JSX.Element;
121
+ export declare function LoginView({ logo, authController, noUserComponent, disableSignupScreen, disabled, notAllowedError, googleClientId, githubClientId, linkedinClientId, title, subtitle, needsSetup, registrationEnabled, additionalComponent, topComponent, defaultEmail, defaultPassword, onNewsletterOptIn }: LoginViewProps): React.JSX.Element;
@@ -1,4 +1,4 @@
1
- import type { RebaseAuthConfig } from "@rebasepro/admin-types";
1
+ import type { RebaseAuthViewConfig } from "@rebasepro/admin-types";
2
2
  /**
3
3
  * Declarative component to configure authentication in Rebase.
4
4
  * Renders nothing — purely registers config into the RebaseRegistry.
@@ -7,4 +7,4 @@ import type { RebaseAuthConfig } from "@rebasepro/admin-types";
7
7
  * authentication is cross-cutting (admin, Studio, any app area).
8
8
  * @group Core
9
9
  */
10
- export declare function RebaseAuth({ loginView }: RebaseAuthConfig): null;
10
+ export declare function RebaseAuth({ loginView }: RebaseAuthViewConfig): null;
@@ -43,10 +43,8 @@ type DeepPartial<T> = T extends object ? {
43
43
  * Controller to simulate different roles when dev mode is active.
44
44
  * @group Models
45
45
  */
46
- export interface EffectiveRoleController {
47
- effectiveRole: string | null;
48
- setEffectiveRole: (role: string | null) => void;
49
- }
46
+ export type { EffectiveRoleController } from "@rebasepro/types";
47
+ import type { EffectiveRoleController } from "@rebasepro/types";
50
48
  /**
51
49
  * Props for the main {@link Rebase} component.
52
50
  *
@@ -289,4 +287,3 @@ export type RebaseProps<USER extends User> = {
289
287
  */
290
288
  components?: ComponentOverrideMap;
291
289
  };
292
- export {};
@@ -1,5 +1,5 @@
1
1
  import React from "react";
2
- import type { RebaseRegistryController, RebaseAdminConfig, RebaseStudioConfig, RebaseAuthConfig } from "@rebasepro/admin-types";
2
+ import type { RebaseRegistryController, RebaseAdminConfig, RebaseStudioConfig, RebaseAuthViewConfig } from "@rebasepro/admin-types";
3
3
  /**
4
4
  * Split into two contexts to prevent infinite re-render loops:
5
5
  * - DispatchContext: stable register/unregister functions (never changes identity)
@@ -14,7 +14,7 @@ interface RegistryDispatch {
14
14
  unregisterAdmin: () => void;
15
15
  registerStudio: (config: RebaseStudioConfig) => void;
16
16
  unregisterStudio: () => void;
17
- registerAuth: (config: RebaseAuthConfig) => void;
17
+ registerAuth: (config: RebaseAuthViewConfig) => void;
18
18
  unregisterAuth: () => void;
19
19
  }
20
20
  export declare function RebaseRegistryProvider({ children }: {
package/dist/index.es.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import React, { createContext, lazy, useCallback, useContext, useEffect, useId, useLayoutEffect, useMemo, useRef, useState } from "react";
2
2
  import { Alert, AlertCircleIcon, AlertTriangleIcon, AppWindow, ArrowDownIcon, ArrowLeftIcon, ArrowRightLeftIcon, ArrowUpDownIcon, ArrowUpIcon, Avatar, BooleanSwitch, Button, CHIP_COLORS, CalendarIcon, Card, CenteredView, CheckCircle2Icon, CheckIcon, Checkbox, ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, ChevronsLeftIcon, ChevronsRightIcon, Chip, CircleDotIcon, CircleUserIcon, CircularProgress, ColumnsIcon, Container, Dialog, DialogActions, DialogContent, DialogTitle, DollarSignIcon, ErrorBoundary, FileIcon, FileTextIcon, FilterChip, FilterIcon, FolderIcon, FolderKanbanIcon, IconButton, KanbanIcon, KanbanView, LanguagesIcon, LayoutGridIcon, ListIcon, ListPlusIcon, ListTodoIcon, LoadingButton, LogOutIcon, MailIcon, Menu, MenuItem, MessageCircleIcon, MoonIcon, MultiSelect, MultiSelectItem, PanelLeftIcon, Paper, PenLineIcon, PencilIcon, PhoneIcon, PinIcon, PlusIcon, Popover, RefreshCwIcon, SearchBar, Select, SelectItem, Separator, SettingsIcon, Skeleton, StickyNoteIcon, SunIcon, SunMoonIcon, Tab, Table, TableBody, TableCell, TableHeader, TableRow, Tabs, TagIcon, TextField, ToggleButtonGroup, Tooltip, Trash2Icon, TrendingUpIcon, TypeIcon, Typography, UserIcon, UserPlus, UsersIcon, VideoIcon, Wand2Icon, WrenchIcon, XIcon, cls, colorClassesMapping, coolIconKeys, defaultBorderMixin, getColorSchemeForSeed, iconKeys, iconSize, lucideIcons } from "@rebasepro/ui";
3
- import { ALL_WHERE_FILTER_OPS, DEFAULT_DATA_SOURCE_KEY, DEFAULT_FILTERABLE_RELATION_KINDS, DEFAULT_STORAGE_SOURCE_KEY, EntityReference, EntityRelation, GeoPoint, RebaseApiError, Vector, getDataSourceCapabilities, isLazyComponentRef } from "@rebasepro/types";
3
+ import { ALL_WHERE_FILTER_OPS, DEFAULT_DATA_SOURCE_KEY, DEFAULT_FILTERABLE_RELATION_KINDS, DEFAULT_STORAGE_SOURCE_KEY, EntityReference, EntityRelation, GeoPoint, RebaseApiError, Vector, getDataSourceCapabilities, isLazyComponentRef, isRelationalCollectionConfig } from "@rebasepro/types";
4
4
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
5
5
  import { SnackbarProvider as SnackbarProvider$1, useSnackbar } from "notistack";
6
6
  import { buildRebaseData, canCreateEntity, canDeleteEntity, canEditEntity, canReadCollection, evaluateCondition, getEntityChildViews, getLabelOrConfigFrom, getPrimaryKeys, getSubcollections, isPropertyBuilder, resolveStorageFilenameString, resolveStoragePathString, resolveStorageSource, stripCollectionPath, wrapAsEntityData, wrapAsSdkData } from "@rebasepro/common";
@@ -8942,12 +8942,13 @@ function warnAboutHalfConfiguredOAuth(provider, clientId, backendProviders) {
8942
8942
  * Feature-detects capabilities to show/hide login methods.
8943
8943
  * @group Core
8944
8944
  */
8945
- function LoginView({ logo, authController, noUserComponent, disableSignupScreen = false, disabled = false, notAllowedError, googleClientId, githubClientId, linkedinClientId, title, subtitle, needsSetup, registrationEnabled, additionalComponent, topComponent, defaultEmail, defaultPassword }) {
8945
+ function LoginView({ logo, authController, noUserComponent, disableSignupScreen = false, disabled = false, notAllowedError, googleClientId, githubClientId, linkedinClientId, title, subtitle, needsSetup, registrationEnabled, additionalComponent, topComponent, defaultEmail, defaultPassword, onNewsletterOptIn }) {
8946
8946
  const { mode: colorMode, setMode: setColorMode } = useModeController();
8947
8947
  const { t } = useTranslation();
8948
8948
  const [mode, setMode] = useState("buttons");
8949
8949
  const [fadeIn, setFadeIn] = useState(false);
8950
8950
  const [viewVisible, setViewVisible] = useState(true);
8951
+ const [newsletterOptIn, setNewsletterOptIn] = useState(false);
8951
8952
  const switchMode = (newMode) => {
8952
8953
  setViewVisible(false);
8953
8954
  setTimeout(() => {
@@ -9069,7 +9070,10 @@ function LoginView({ logo, authController, noUserComponent, disableSignupScreen
9069
9070
  disableSignupScreen: false,
9070
9071
  bootstrapMode: true,
9071
9072
  defaultEmail,
9072
- defaultPassword
9073
+ defaultPassword,
9074
+ onNewsletterOptIn,
9075
+ newsletterOptIn,
9076
+ setNewsletterOptIn
9073
9077
  }), !isBootstrapMode && /* @__PURE__ */ jsxs(Fragment, { children: [
9074
9078
  mode === "buttons" && /* @__PURE__ */ jsxs("div", {
9075
9079
  className: "w-full flex flex-col gap-3 mt-2",
@@ -9138,7 +9142,10 @@ function LoginView({ logo, authController, noUserComponent, disableSignupScreen
9138
9142
  disableSignupScreen,
9139
9143
  switchToRegister: showRegistration ? () => switchMode("register") : void 0,
9140
9144
  defaultEmail,
9141
- defaultPassword
9145
+ defaultPassword,
9146
+ onNewsletterOptIn,
9147
+ newsletterOptIn,
9148
+ setNewsletterOptIn
9142
9149
  }),
9143
9150
  mode === "register" && /* @__PURE__ */ jsx(LoginForm, {
9144
9151
  authController,
@@ -9149,7 +9156,10 @@ function LoginView({ logo, authController, noUserComponent, disableSignupScreen
9149
9156
  disableSignupScreen,
9150
9157
  switchToLogin: () => switchMode("login"),
9151
9158
  defaultEmail,
9152
- defaultPassword
9159
+ defaultPassword,
9160
+ onNewsletterOptIn,
9161
+ newsletterOptIn,
9162
+ setNewsletterOptIn
9153
9163
  }),
9154
9164
  mode === "forgot" && authController.forgotPassword && /* @__PURE__ */ jsx(ForgotPasswordForm, {
9155
9165
  authController,
@@ -9289,8 +9299,9 @@ function LinkedInLoginButton({ disabled, linkedinClientId }) {
9289
9299
  onClick: handleClick
9290
9300
  });
9291
9301
  }
9292
- function LoginForm({ onClose, onForgotPassword, authController, registrationMode, noUserComponent, disableSignupScreen, bootstrapMode = false, switchToRegister, switchToLogin, defaultEmail, defaultPassword }) {
9302
+ function LoginForm({ onClose, onForgotPassword, authController, registrationMode, noUserComponent, disableSignupScreen, bootstrapMode = false, switchToRegister, switchToLogin, defaultEmail, defaultPassword, onNewsletterOptIn, newsletterOptIn = false, setNewsletterOptIn }) {
9293
9303
  const passwordRef = useRef(null);
9304
+ const { t } = useTranslation();
9294
9305
  const [email, setEmail] = useState(defaultEmail);
9295
9306
  const [password, setPassword] = useState(defaultPassword);
9296
9307
  const [displayName, setDisplayName] = useState();
@@ -9304,11 +9315,14 @@ function LoginForm({ onClose, onForgotPassword, authController, registrationMode
9304
9315
  document.removeEventListener("keydown", escFunction, false);
9305
9316
  };
9306
9317
  }, [onClose]);
9318
+ function subscribeIfOptedIn(email) {
9319
+ if (newsletterOptIn && onNewsletterOptIn) onNewsletterOptIn(email);
9320
+ }
9307
9321
  function handleEnterPassword() {
9308
- if (email && password && authController.emailPasswordLogin) Promise.resolve(authController.emailPasswordLogin(email, password)).catch(() => void 0);
9322
+ if (email && password && authController.emailPasswordLogin) Promise.resolve(authController.emailPasswordLogin(email, password)).then(() => subscribeIfOptedIn(email)).catch(() => void 0);
9309
9323
  }
9310
9324
  function handleRegistration() {
9311
- if (email && password && authController.register) Promise.resolve(authController.register(email, password, displayName)).catch(() => void 0);
9325
+ if (email && password && authController.register) Promise.resolve(authController.register(email, password, displayName)).then(() => subscribeIfOptedIn(email)).catch(() => void 0);
9312
9326
  }
9313
9327
  const handleSubmit = (event) => {
9314
9328
  event.preventDefault();
@@ -9420,6 +9434,19 @@ function LoginForm({ onClose, onForgotPassword, authController, registrationMode
9420
9434
  children: "Forgot password?"
9421
9435
  })
9422
9436
  }),
9437
+ onNewsletterOptIn && /* @__PURE__ */ jsxs("label", {
9438
+ className: "flex items-center gap-2 cursor-pointer mt-1 mb-1",
9439
+ children: [/* @__PURE__ */ jsx(Checkbox, {
9440
+ checked: newsletterOptIn,
9441
+ onCheckedChange: (checked) => setNewsletterOptIn?.(checked === true),
9442
+ size: "small"
9443
+ }), /* @__PURE__ */ jsx(Typography, {
9444
+ variant: "caption",
9445
+ color: "secondary",
9446
+ className: "select-none",
9447
+ children: t("join_newsletter")
9448
+ })]
9449
+ }),
9423
9450
  /* @__PURE__ */ jsx(LoadingButton, {
9424
9451
  type: "submit",
9425
9452
  variant: "filled",
@@ -9680,6 +9707,7 @@ var en = {
9680
9707
  dark_mode: "Dark",
9681
9708
  light_mode: "Light",
9682
9709
  system_mode: "System",
9710
+ join_newsletter: "Join the newsletter, no spam",
9683
9711
  ok: "Ok",
9684
9712
  save_collection_config: "Save collection structure",
9685
9713
  search_for_more_icons: "Search for more icons…",
@@ -10560,6 +10588,7 @@ var es = {
10560
10588
  dark_mode: "Oscuro",
10561
10589
  light_mode: "Claro",
10562
10590
  system_mode: "Sistema",
10591
+ join_newsletter: "Únete a la newsletter, sin spam",
10563
10592
  ok: "Aceptar",
10564
10593
  save_collection_config: "Guardar la estructura de la colección",
10565
10594
  search_for_more_icons: "Buscar más iconos…",
@@ -11397,6 +11426,7 @@ var de = {
11397
11426
  dark_mode: "Dunkel",
11398
11427
  light_mode: "Hell",
11399
11428
  system_mode: "System",
11429
+ join_newsletter: "Newsletter abonnieren, kein Spam",
11400
11430
  ok: "Ok",
11401
11431
  save_collection_config: "Sammlungsstruktur speichern",
11402
11432
  search_for_more_icons: "Nach weiteren Symbolen suchen…",
@@ -12224,6 +12254,7 @@ var fr = {
12224
12254
  dark_mode: "Sombre",
12225
12255
  light_mode: "Clair",
12226
12256
  system_mode: "Système",
12257
+ join_newsletter: "Rejoignez la newsletter, pas de spam",
12227
12258
  ok: "Ok",
12228
12259
  save_collection_config: "Enregistrer la structure de la collection",
12229
12260
  search_for_more_icons: "Rechercher d'autres icônes…",
@@ -13051,6 +13082,7 @@ var it = {
13051
13082
  dark_mode: "Scuro",
13052
13083
  light_mode: "Chiaro",
13053
13084
  system_mode: "Sistema",
13085
+ join_newsletter: "Iscriviti alla newsletter, niente spam",
13054
13086
  ok: "Ok",
13055
13087
  save_collection_config: "Salva struttura della collezione",
13056
13088
  search_for_more_icons: "Cerca altre icone…",
@@ -13878,6 +13910,7 @@ var hi = {
13878
13910
  dark_mode: "डार्क",
13879
13911
  light_mode: "लाइट",
13880
13912
  system_mode: "सिस्टम",
13913
+ join_newsletter: "न्यूज़लेटर से जुड़ें, कोई स्पैम नहीं",
13881
13914
  ok: "ठीक है",
13882
13915
  save_collection_config: "संग्रह संरचना सहेजें",
13883
13916
  search_for_more_icons: "और आइकन खोजें…",
@@ -14710,6 +14743,7 @@ var pt = {
14710
14743
  dark_mode: "Escuro",
14711
14744
  light_mode: "Claro",
14712
14745
  system_mode: "Sistema",
14746
+ join_newsletter: "Assine a newsletter, sem spam",
14713
14747
  ok: "Ok",
14714
14748
  save_collection_config: "Guardar estrutura da coleção",
14715
14749
  search_for_more_icons: "Pesquisar mais ícones…",
@@ -17478,11 +17512,20 @@ function applyPropertyConditions(property, context) {
17478
17512
  if ("enum" in result && result.enum && (conditions.enumConditions || conditions.allowedEnumValues || conditions.excludedEnumValues)) result.enum = applyEnumConditions(result.enum, conditions, context);
17479
17513
  if (result.type === "reference") {
17480
17514
  if (conditions.referencePath) result.path = evaluateCondition(conditions.referencePath, context);
17481
- if (conditions.referenceFilter) result.fixedFilter = evaluateCondition(conditions.referenceFilter, context);
17515
+ if (conditions.referenceFilter) {
17516
+ result.admin = result.admin || {};
17517
+ result.admin.fixedFilter = evaluateCondition(conditions.referenceFilter, context);
17518
+ }
17482
17519
  }
17483
17520
  if (result.type === "array") {
17484
- if (conditions.canAddElements !== void 0) result.canAddElements = evaluateCondition(conditions.canAddElements, context);
17485
- if (conditions.sortable !== void 0) result.sortable = evaluateCondition(conditions.sortable, context);
17521
+ if (conditions.canAddElements !== void 0) {
17522
+ result.admin = result.admin || {};
17523
+ result.admin.canAddElements = evaluateCondition(conditions.canAddElements, context);
17524
+ }
17525
+ if (conditions.sortable !== void 0) {
17526
+ result.admin = result.admin || {};
17527
+ result.admin.sortable = evaluateCondition(conditions.sortable, context);
17528
+ }
17486
17529
  }
17487
17530
  return result;
17488
17531
  }
@@ -17612,7 +17655,7 @@ function getForeignKeyColumns(collection) {
17612
17655
  default: break;
17613
17656
  }
17614
17657
  };
17615
- for (const relation of collection.relations ?? []) addRelationKeys(relation);
17658
+ for (const relation of (isRelationalCollectionConfig(collection) ? collection.relations : void 0) ?? []) addRelationKeys(relation);
17616
17659
  for (const [key, propertyRaw] of Object.entries(collection.properties ?? {})) {
17617
17660
  const property = propertyRaw;
17618
17661
  if (!property || isPropertyBuilder(property) || property.type !== "relation") continue;