@rebasepro/app 0.17.3 → 0.18.1
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.
- package/README.md +4 -1
- package/dist/components/index.d.ts +0 -3
- package/dist/contexts/AuthControllerContext.d.ts +2 -1
- package/dist/contexts/CustomizationControllerContext.d.ts +2 -1
- package/dist/contexts/DialogsProvider.d.ts +2 -1
- package/dist/contexts/RebaseDataContext.d.ts +10 -1
- package/dist/contexts/StorageSourceContext.d.ts +11 -1
- package/dist/core/RebaseProps.d.ts +2 -12
- package/dist/debug/index.d.ts +19 -0
- package/dist/debug.js +3631 -0
- package/dist/hooks/data/delete.d.ts +16 -7
- package/dist/hooks/data/save.d.ts +18 -4
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/useAdminModeController.d.ts +5 -4
- package/dist/hooks/useNavigationGroupLabel.d.ts +17 -0
- package/dist/hooks/useStudioBridge.d.ts +33 -0
- package/dist/index.es.js +2599 -6980
- package/dist/index.es.js.map +1 -1
- package/dist/vitePlugin.d.ts +22 -0
- package/dist/vitePlugin.js +40 -1
- package/package.json +42 -31
- package/src/auth/api.ts +0 -134
- package/src/auth/index.ts +0 -26
- package/src/auth/types.ts +0 -115
- package/src/auth/useRebaseAuthController.ts +0 -345
- package/src/collections/collection_view_config.ts +0 -76
- package/src/collections/entity-display-cache.ts +0 -195
- package/src/collections/entity-display.ts +0 -77
- package/src/collections/entity_image_preview.ts +0 -61
- package/src/collections/filter-operator-resolution.ts +0 -119
- package/src/collections/form-layout.ts +0 -394
- package/src/collections/index.ts +0 -13
- package/src/collections/navigation_from_path.ts +0 -123
- package/src/collections/navigation_utils.ts +0 -171
- package/src/collections/parent_references_from_path.ts +0 -59
- package/src/collections/property-path.ts +0 -30
- package/src/collections/property_presentation.ts +0 -264
- package/src/collections/summary-property.ts +0 -114
- package/src/collections/title-property.ts +0 -312
- package/src/components/AIIcon.tsx +0 -47
- package/src/components/AuthSimulationMenu.tsx +0 -0
- package/src/components/ConfirmationDialog.tsx +0 -48
- package/src/components/Debug/UIReferenceView.tsx +0 -1053
- package/src/components/Debug/UIStyleGuide.tsx +0 -159
- package/src/components/Debug/collection-views/CardViewDemo.tsx +0 -87
- package/src/components/Debug/collection-views/CollectionTableDemo.tsx +0 -107
- package/src/components/Debug/collection-views/KanbanBoardDemo.tsx +0 -89
- package/src/components/Debug/collection-views/index.ts +0 -3
- package/src/components/Debug/collection-views/sample_data.ts +0 -44
- package/src/components/Debug/crm-dashboard/CalendarWidget.tsx +0 -387
- package/src/components/Debug/crm-dashboard/CrmDashboardDemo.tsx +0 -1100
- package/src/components/Debug/crm-dashboard/DashboardMetrics.tsx +0 -131
- package/src/components/Debug/crm-dashboard/PipelineOverview.tsx +0 -288
- package/src/components/Debug/crm-dashboard/RecentActivity.tsx +0 -213
- package/src/components/Debug/crm-dashboard/TaskFilters.tsx +0 -182
- package/src/components/Debug/crm-dashboard/TaskTable.tsx +0 -425
- package/src/components/Debug/crm-dashboard/TasksView.tsx +0 -234
- package/src/components/ErrorTooltip.tsx +0 -12
- package/src/components/ErrorView.tsx +0 -94
- package/src/components/LanguageToggle.tsx +0 -59
- package/src/components/LoginView/LoginView.tsx +0 -1235
- package/src/components/LoginView/index.ts +0 -2
- package/src/components/LoginView/oauth-redirect-flow.ts +0 -184
- package/src/components/NotFoundPage.tsx +0 -27
- package/src/components/RebaseAuth.tsx +0 -27
- package/src/components/RebaseLogo.tsx +0 -88
- package/src/components/SchemaDriftBanner.tsx +0 -102
- package/src/components/UnsavedChangesDialog.tsx +0 -46
- package/src/components/UserDisplay.tsx +0 -54
- package/src/components/UserSelectPopover.tsx +0 -399
- package/src/components/UserSettingsView.tsx +0 -318
- package/src/components/common/index.ts +0 -6
- package/src/components/common/table_height.tsx +0 -21
- package/src/components/common/types.tsx +0 -66
- package/src/components/common/useColumnsIds.tsx +0 -261
- package/src/components/common/useDataTableController.tsx +0 -571
- package/src/components/common/useDebouncedData.ts +0 -49
- package/src/components/common/useScrollRestoration.tsx +0 -68
- package/src/components/index.tsx +0 -26
- package/src/contexts/AdminModeController.tsx +0 -11
- package/src/contexts/AnalyticsContext.tsx +0 -4
- package/src/contexts/AuthControllerContext.tsx +0 -4
- package/src/contexts/CollectionResolverContext.tsx +0 -24
- package/src/contexts/CollectionScopeContext.tsx +0 -68
- package/src/contexts/ComponentOverrideContext.tsx +0 -81
- package/src/contexts/CustomizationControllerContext.tsx +0 -4
- package/src/contexts/DataDriverContext.tsx +0 -4
- package/src/contexts/DataSourcesContext.tsx +0 -36
- package/src/contexts/DatabaseAdminContext.tsx +0 -4
- package/src/contexts/DialogsProvider.tsx +0 -56
- package/src/contexts/EffectiveRoleController.tsx +0 -12
- package/src/contexts/ModeController.tsx +0 -11
- package/src/contexts/RebaseClientInstanceContext.tsx +0 -7
- package/src/contexts/RebaseDataContext.tsx +0 -4
- package/src/contexts/SnackbarProvider.tsx +0 -15
- package/src/contexts/StorageSourceContext.tsx +0 -4
- package/src/contexts/StorageSourcesContext.tsx +0 -30
- package/src/contexts/UserConfigurationPersistenceContext.tsx +0 -4
- package/src/contexts/index.ts +0 -18
- package/src/core/PluginLifecycleManager.tsx +0 -96
- package/src/core/PluginProviderStack.tsx +0 -40
- package/src/core/Rebase.tsx +0 -488
- package/src/core/RebaseProps.tsx +0 -333
- package/src/core/RebaseRouter.tsx +0 -18
- package/src/core/RebaseRoutes.tsx +0 -24
- package/src/core/index.tsx +0 -5
- package/src/hooks/ApiConfigContext.tsx +0 -83
- package/src/hooks/data/collectionQuery.ts +0 -55
- package/src/hooks/data/delete.ts +0 -56
- package/src/hooks/data/save.ts +0 -78
- package/src/hooks/data/useCollection.tsx +0 -183
- package/src/hooks/data/useData.tsx +0 -18
- package/src/hooks/data/useDataOrder.ts +0 -26
- package/src/hooks/data/useFetch.tsx +0 -156
- package/src/hooks/data/useRelationSelector.tsx +0 -280
- package/src/hooks/index.tsx +0 -45
- package/src/hooks/useAdminModeController.tsx +0 -23
- package/src/hooks/useAnalyticsController.tsx +0 -8
- package/src/hooks/useAuthController.tsx +0 -15
- package/src/hooks/useAuthSubscription.ts +0 -130
- package/src/hooks/useBackendStorageSource.ts +0 -295
- package/src/hooks/useBridgeRegistration.tsx +0 -32
- package/src/hooks/useBrowserTitleAndIcon.tsx +0 -23
- package/src/hooks/useBuildAdminModeController.tsx +0 -29
- package/src/hooks/useBuildEffectiveRoleController.tsx +0 -30
- package/src/hooks/useBuildLocalConfigurationPersistence.tsx +0 -83
- package/src/hooks/useBuildModeController.tsx +0 -73
- package/src/hooks/useClipboard.tsx +0 -158
- package/src/hooks/useCollapsedGroups.ts +0 -116
- package/src/hooks/useComponentOverride.tsx +0 -59
- package/src/hooks/useCustomizationController.tsx +0 -14
- package/src/hooks/useDialogsController.tsx +0 -15
- package/src/hooks/useEffectiveRoleController.tsx +0 -10
- package/src/hooks/useLargeLayout.tsx +0 -65
- package/src/hooks/useModeController.tsx +0 -23
- package/src/hooks/useNavigationBlocker.tsx +0 -108
- package/src/hooks/usePermissions.ts +0 -45
- package/src/hooks/useRebaseClient.tsx +0 -10
- package/src/hooks/useRebaseContext.tsx +0 -84
- package/src/hooks/useRebaseRegistry.tsx +0 -91
- package/src/hooks/useResolvedComponent.tsx +0 -195
- package/src/hooks/useSlot.tsx +0 -64
- package/src/hooks/useSnackbarController.tsx +0 -80
- package/src/hooks/useStorageSource.tsx +0 -11
- package/src/hooks/useStudioBridge.tsx +0 -258
- package/src/hooks/useTranslation.ts +0 -38
- package/src/hooks/useUnsavedChangesDialog.tsx +0 -67
- package/src/hooks/useUserConfigurationPersistence.tsx +0 -11
- package/src/i18n/RebaseI18nProvider.tsx +0 -159
- package/src/index.ts +0 -32
- package/src/internal/common.tsx +0 -28
- package/src/internal/useRestoreScroll.tsx +0 -69
- package/src/locales/de.ts +0 -941
- package/src/locales/en.ts +0 -1021
- package/src/locales/es.ts +0 -962
- package/src/locales/fr.ts +0 -941
- package/src/locales/hi.ts +0 -941
- package/src/locales/it.ts +0 -941
- package/src/locales/pt.ts +0 -949
- package/src/util/constants.ts +0 -7
- package/src/util/createFormexStub.tsx +0 -66
- package/src/util/entity_cache.ts +0 -242
- package/src/util/enums.ts +0 -34
- package/src/util/icon_list.ts +0 -22
- package/src/util/icons.tsx +0 -129
- package/src/util/index.ts +0 -13
- package/src/util/local_storage.ts +0 -66
- package/src/util/previews.ts +0 -127
- package/src/util/useStorageUploadController.tsx +0 -354
- package/src/vitePlugin.ts +0 -215
- /package/dist/{components/Debug → debug}/UIReferenceView.d.ts +0 -0
- /package/dist/{components/Debug → debug}/UIStyleGuide.d.ts +0 -0
- /package/dist/{components/Debug → debug}/collection-views/CardViewDemo.d.ts +0 -0
- /package/dist/{components/Debug → debug}/collection-views/CollectionTableDemo.d.ts +0 -0
- /package/dist/{components/Debug → debug}/collection-views/KanbanBoardDemo.d.ts +0 -0
- /package/dist/{components/Debug → debug}/collection-views/index.d.ts +0 -0
- /package/dist/{components/Debug → debug}/collection-views/sample_data.d.ts +0 -0
- /package/dist/{components/Debug → debug}/crm-dashboard/CalendarWidget.d.ts +0 -0
- /package/dist/{components/Debug → debug}/crm-dashboard/CrmDashboardDemo.d.ts +0 -0
- /package/dist/{components/Debug → debug}/crm-dashboard/DashboardMetrics.d.ts +0 -0
- /package/dist/{components/Debug → debug}/crm-dashboard/PipelineOverview.d.ts +0 -0
- /package/dist/{components/Debug → debug}/crm-dashboard/RecentActivity.d.ts +0 -0
- /package/dist/{components/Debug → debug}/crm-dashboard/TaskFilters.d.ts +0 -0
- /package/dist/{components/Debug → debug}/crm-dashboard/TaskTable.d.ts +0 -0
- /package/dist/{components/Debug → debug}/crm-dashboard/TasksView.d.ts +0 -0
|
@@ -1,184 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The browser half of the OAuth authorization-code flow.
|
|
3
|
-
*
|
|
4
|
-
* The server only ever sees a `code` and a `redirectUri` taken from the
|
|
5
|
-
* request body, so `state`, PKCE and the binding between "this browser started
|
|
6
|
-
* a login" and "this code came back" are, by construction, the client's job.
|
|
7
|
-
* In the admin login they had been nobody's job: the authorize URL carried no
|
|
8
|
-
* `state`, and the return leg accepted any `?code=` as long as a localStorage
|
|
9
|
-
* marker naming the *provider* was set — a marker written on button click and
|
|
10
|
-
* removed only on a callback that carried a code, so abandoning the flow left
|
|
11
|
-
* it set indefinitely.
|
|
12
|
-
*
|
|
13
|
-
* Everything here is provider-agnostic on purpose. A button that wants a new
|
|
14
|
-
* provider calls {@link startOAuthRedirect} and gets state, expiry and PKCE
|
|
15
|
-
* without deciding anything, which is what stops the next provider shipping
|
|
16
|
-
* without them.
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
|
-
/** sessionStorage, not localStorage: a login attempt belongs to one tab. */
|
|
20
|
-
const STORAGE_KEY = "rebase_oauth_redirect";
|
|
21
|
-
|
|
22
|
-
/** How long a started authorization stays valid. */
|
|
23
|
-
const PENDING_TTL_MS = 10 * 60 * 1000;
|
|
24
|
-
|
|
25
|
-
export interface OAuthRedirectRequest {
|
|
26
|
-
/** Provider id, as mounted at `POST /auth/<id>`. */
|
|
27
|
-
provider: string;
|
|
28
|
-
/** The provider's authorization endpoint. */
|
|
29
|
-
authorizeUrl: string;
|
|
30
|
-
clientId: string;
|
|
31
|
-
scope: string;
|
|
32
|
-
/** Where the provider sends the browser back. Echoed to the token exchange. */
|
|
33
|
-
redirectUri: string;
|
|
34
|
-
/** Set when the provider implements PKCE. */
|
|
35
|
-
pkce?: boolean;
|
|
36
|
-
/** Extra authorize-endpoint parameters (`response_type`, and so on). */
|
|
37
|
-
params?: Record<string, string>;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/** What the browser remembers between leaving for the provider and coming back. */
|
|
41
|
-
export interface PendingOAuthRedirect {
|
|
42
|
-
provider: string;
|
|
43
|
-
state: string;
|
|
44
|
-
codeVerifier?: string;
|
|
45
|
-
redirectUri: string;
|
|
46
|
-
startedAt: number;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export type OAuthCallbackResult =
|
|
50
|
-
/** No OAuth callback in this URL. */
|
|
51
|
-
| { status: "none" }
|
|
52
|
-
/** The provider reported a failure, or the user declined. */
|
|
53
|
-
| { status: "error"; error: string }
|
|
54
|
-
/**
|
|
55
|
-
* A code arrived that this browser cannot account for: no pending
|
|
56
|
-
* authorization, an expired one, or a `state` that does not match. This is
|
|
57
|
-
* what a login-CSRF / code-injection attempt looks like.
|
|
58
|
-
*/
|
|
59
|
-
| { status: "mismatch" }
|
|
60
|
-
| { status: "ok"; provider: string; code: string; codeVerifier?: string; redirectUri: string };
|
|
61
|
-
|
|
62
|
-
type Storage = Pick<globalThis.Storage, "getItem" | "setItem" | "removeItem">;
|
|
63
|
-
|
|
64
|
-
function defaultStorage(): Storage | null {
|
|
65
|
-
try {
|
|
66
|
-
return window.sessionStorage;
|
|
67
|
-
} catch {
|
|
68
|
-
return null;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function base64Url(bytes: Uint8Array): string {
|
|
73
|
-
let binary = "";
|
|
74
|
-
for (const byte of bytes) binary += String.fromCharCode(byte);
|
|
75
|
-
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function randomToken(byteLength = 32): string {
|
|
79
|
-
const bytes = new Uint8Array(byteLength);
|
|
80
|
-
crypto.getRandomValues(bytes);
|
|
81
|
-
return base64Url(bytes);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
async function pkceChallenge(verifier: string): Promise<string> {
|
|
85
|
-
const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(verifier));
|
|
86
|
-
return base64Url(new Uint8Array(digest));
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
export function readPendingOAuthRedirect(storage: Storage | null = defaultStorage()): PendingOAuthRedirect | null {
|
|
90
|
-
if (!storage) return null;
|
|
91
|
-
const raw = storage.getItem(STORAGE_KEY);
|
|
92
|
-
if (!raw) return null;
|
|
93
|
-
try {
|
|
94
|
-
const parsed = JSON.parse(raw) as PendingOAuthRedirect;
|
|
95
|
-
if (!parsed?.provider || !parsed?.state) return null;
|
|
96
|
-
return parsed;
|
|
97
|
-
} catch {
|
|
98
|
-
return null;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
export function clearPendingOAuthRedirect(storage: Storage | null = defaultStorage()): void {
|
|
103
|
-
storage?.removeItem(STORAGE_KEY);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* Build the authorize URL and the pending record that will validate its
|
|
108
|
-
* callback. Split out from {@link startOAuthRedirect} so it can be tested
|
|
109
|
-
* without navigating.
|
|
110
|
-
*/
|
|
111
|
-
export async function buildOAuthAuthorization(
|
|
112
|
-
request: OAuthRedirectRequest,
|
|
113
|
-
now: number = Date.now()
|
|
114
|
-
): Promise<{ url: string; pending: PendingOAuthRedirect }> {
|
|
115
|
-
const state = randomToken();
|
|
116
|
-
const url = new URL(request.authorizeUrl);
|
|
117
|
-
url.searchParams.set("response_type", "code");
|
|
118
|
-
url.searchParams.set("client_id", request.clientId);
|
|
119
|
-
url.searchParams.set("redirect_uri", request.redirectUri);
|
|
120
|
-
url.searchParams.set("scope", request.scope);
|
|
121
|
-
url.searchParams.set("state", state);
|
|
122
|
-
for (const [key, value] of Object.entries(request.params ?? {})) {
|
|
123
|
-
url.searchParams.set(key, value);
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
let codeVerifier: string | undefined;
|
|
127
|
-
if (request.pkce) {
|
|
128
|
-
codeVerifier = randomToken(32);
|
|
129
|
-
url.searchParams.set("code_challenge", await pkceChallenge(codeVerifier));
|
|
130
|
-
url.searchParams.set("code_challenge_method", "S256");
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
return {
|
|
134
|
-
url: url.toString(),
|
|
135
|
-
pending: { provider: request.provider, state, codeVerifier, redirectUri: request.redirectUri, startedAt: now }
|
|
136
|
-
};
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
/** Remember the authorization and send the browser to the provider. */
|
|
140
|
-
export async function startOAuthRedirect(
|
|
141
|
-
request: OAuthRedirectRequest,
|
|
142
|
-
storage: Storage | null = defaultStorage()
|
|
143
|
-
): Promise<void> {
|
|
144
|
-
const { url, pending } = await buildOAuthAuthorization(request);
|
|
145
|
-
storage?.setItem(STORAGE_KEY, JSON.stringify(pending));
|
|
146
|
-
window.location.href = url;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* Interpret a return from the provider.
|
|
151
|
-
*
|
|
152
|
-
* Clears the pending record on **every** path that saw a callback — including
|
|
153
|
-
* `?error=` and a state mismatch — so a declined or abandoned consent screen
|
|
154
|
-
* cannot leave the browser primed to accept somebody else's code later.
|
|
155
|
-
*/
|
|
156
|
-
export function consumeOAuthCallback(
|
|
157
|
-
search: string,
|
|
158
|
-
storage: Storage | null = defaultStorage(),
|
|
159
|
-
now: number = Date.now()
|
|
160
|
-
): OAuthCallbackResult {
|
|
161
|
-
const params = new URLSearchParams(search);
|
|
162
|
-
const code = params.get("code");
|
|
163
|
-
const state = params.get("state");
|
|
164
|
-
const error = params.get("error");
|
|
165
|
-
|
|
166
|
-
if (!code && !error && !state) return { status: "none" };
|
|
167
|
-
|
|
168
|
-
const pending = readPendingOAuthRedirect(storage);
|
|
169
|
-
clearPendingOAuthRedirect(storage);
|
|
170
|
-
|
|
171
|
-
if (error) return { status: "error", error };
|
|
172
|
-
if (!code) return { status: "mismatch" };
|
|
173
|
-
if (!pending) return { status: "mismatch" };
|
|
174
|
-
if (now - pending.startedAt > PENDING_TTL_MS) return { status: "mismatch" };
|
|
175
|
-
if (!state || state !== pending.state) return { status: "mismatch" };
|
|
176
|
-
|
|
177
|
-
return {
|
|
178
|
-
status: "ok",
|
|
179
|
-
provider: pending.provider,
|
|
180
|
-
code,
|
|
181
|
-
codeVerifier: pending.codeVerifier,
|
|
182
|
-
redirectUri: pending.redirectUri
|
|
183
|
-
};
|
|
184
|
-
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import { Link } from "react-router";
|
|
3
|
-
import { Button, Typography } from "@rebasepro/ui";
|
|
4
|
-
import { useTranslation } from "../hooks";
|
|
5
|
-
|
|
6
|
-
export function NotFoundPage() {
|
|
7
|
-
const { t } = useTranslation();
|
|
8
|
-
|
|
9
|
-
return (
|
|
10
|
-
<div className="flex w-full h-full">
|
|
11
|
-
<div className="m-auto flex items-center flex-col"
|
|
12
|
-
>
|
|
13
|
-
<Typography variant={"h4"} align={"center"}
|
|
14
|
-
gutterBottom={true}>
|
|
15
|
-
{t("page_not_found")}
|
|
16
|
-
</Typography>
|
|
17
|
-
<Typography align={"center"} gutterBottom={true}>
|
|
18
|
-
{t("page_not_found_body")}
|
|
19
|
-
</Typography>
|
|
20
|
-
<Button
|
|
21
|
-
variant={"text"}
|
|
22
|
-
component={Link}
|
|
23
|
-
to={"/"}>{t("back_to_home")}</Button>
|
|
24
|
-
</div>
|
|
25
|
-
</div>
|
|
26
|
-
);
|
|
27
|
-
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import React, { useLayoutEffect, useRef } from "react";
|
|
2
|
-
import { useRebaseRegistryDispatch } from "../hooks";
|
|
3
|
-
import type { RebaseAuthViewConfig } from "@rebasepro/cms-types";
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Declarative component to configure authentication in Rebase.
|
|
7
|
-
* Renders nothing — purely registers config into the RebaseRegistry.
|
|
8
|
-
*
|
|
9
|
-
* This is a framework-level component that lives in core since
|
|
10
|
-
* authentication is cross-cutting (admin, Studio, any app area).
|
|
11
|
-
* @group Core
|
|
12
|
-
*/
|
|
13
|
-
export function RebaseAuth({ loginView }: RebaseAuthViewConfig) {
|
|
14
|
-
const dispatch = useRebaseRegistryDispatch();
|
|
15
|
-
const registeredRef = useRef(false);
|
|
16
|
-
|
|
17
|
-
useLayoutEffect(() => {
|
|
18
|
-
dispatch.registerAuth({ loginView });
|
|
19
|
-
registeredRef.current = true;
|
|
20
|
-
return () => {
|
|
21
|
-
registeredRef.current = false;
|
|
22
|
-
dispatch.unregisterAuth();
|
|
23
|
-
};
|
|
24
|
-
}, [dispatch, loginView]);
|
|
25
|
-
|
|
26
|
-
return null;
|
|
27
|
-
}
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
|
|
3
|
-
export function RebaseLogo({
|
|
4
|
-
width,
|
|
5
|
-
height,
|
|
6
|
-
className,
|
|
7
|
-
style
|
|
8
|
-
}: {
|
|
9
|
-
width?: string,
|
|
10
|
-
height?: string,
|
|
11
|
-
className?: string,
|
|
12
|
-
style?: React.CSSProperties
|
|
13
|
-
}) {
|
|
14
|
-
return (
|
|
15
|
-
<svg
|
|
16
|
-
width={width ?? "100%"} height={height ?? "100%"}
|
|
17
|
-
version="1.1"
|
|
18
|
-
style={style}
|
|
19
|
-
className={className}
|
|
20
|
-
viewBox="112.1405 1079.711 306.058 306.058" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
21
|
-
<path fill="#34b6d9" d="M237.01 1087.234a56 56 0 0 1 56.07 0l83.819 48.473a56 56 0 0 1 27.965 48.477v97.108a56 56 0 0 1-27.965 48.477l-83.819 48.473a56 56 0 0 1-56.07 0l-83.818-48.473a56 56 0 0 1-27.965-48.477v-97.108a56 56 0 0 1 27.965-48.477Z"/>
|
|
22
|
-
<path fill="#358fd8" d="M264.64 1286.718c27.352 0 49.525 22.173 49.525 49.526 0 27.352-22.173 49.525-49.525 49.525-27.353 0-49.526-22.173-49.526-49.525Z"/>
|
|
23
|
-
<path fill="#ff9197" d="M174.941 1131.547c27.352 0 49.526 22.174 49.526 49.526s-22.174 49.526-49.526 49.526-49.526-22.174-49.526-49.526Z"/>
|
|
24
|
-
<path fill="#358fd8" d="M355.338 1131.547c27.352 0 49.526 22.174 49.526 49.526s-22.174 49.526-49.526 49.526-49.526-22.174-49.526-49.526Z"/>
|
|
25
|
-
<path fill="#fe9197" d="M125.227 1196.44v-14.661c0-13.35 7.55-20.303 17.99-18.168l56.424 31.13 2.72 1.572 27.643 31.545 35.193 30.52q.173 1.522.229 3.013l-54.046-11.395-28.464-26.045Z"/>
|
|
26
|
-
<path fill="#013afd" d="M151.207 1328.696c-14.348-8.285-25.98-28.432-25.98-45v-35.214l11.299-.401c.614.866 34.062 48.973 34.606 49.656.394.495 22.156 40.224 34.409 62.328Z"/>
|
|
27
|
-
<path fill="#2c09d1" d="m211.38 1249.996 30.436 6.417 9.53 9.1-2.926 15.797 16.939 30.08-14.049 6.672-35.072-10.288-33.83-61.32 9.09-.323Z"/>
|
|
28
|
-
<path fill="#2ac1cf" d="M240.774 1380.37a34 34 0 0 1-1.304-.716l-16.141-9.319-49.383-74.238 29.211 7.839Z"/>
|
|
29
|
-
<path fill="#2c42d0" d="m173.359 1219.393 9.557 4.558 28.464 26.045-19.883-3.865-12.782.453-31.643-13.936-15.11-11.819Z"/>
|
|
30
|
-
<path fill="#8b66ed" d="M257.113 1381.863c-4.336 2.13-10.043 1.783-16.34-1.493l-37.616-76.434 13.081 3.838Z"/>
|
|
31
|
-
<path fill="#2c09d1" d="m145.888 1247.749 7.224-.257 23.615 41.804 11.98 1.728-24.421-43.544-.014-.383 10.725-.38 28.16 57.219-29.211-7.839-7.37-11.08Z"/>
|
|
32
|
-
<path fill="#2ac1cf" d="M265.451 1311.554v53.1q0 .255-.004.505l-.25-.26-29.148-38.794-6.308-14.37 21.57 6.328 14.048-6.672Z"/>
|
|
33
|
-
<path fill="#fd485f" d="m223.329 1370.335-17.788-10.27c-12.253-22.104-34.015-61.833-34.41-62.328-.543-.683-33.991-48.79-34.605-49.656l9.362-.332 20.688 37.269Z"/>
|
|
34
|
-
<path fill="#2d42d0" d="m202.362 1196.313 37.108 21.424c13.11 7.57 23.953 25.043 25.727 40.641l-35.193-30.52Z"/>
|
|
35
|
-
<path fill="#2b0ad1" d="M265.447 1365.16c-.095 6.58-2.056 11.56-5.304 14.604l-39.936-70.826 9.534 2.797 6.308 14.37 29.148 38.794Z"/>
|
|
36
|
-
<path fill="#fe9196" d="M125.227 1240.59v-16.4l21.845 8.458 31.643 13.936-35.367 1.255Z"/>
|
|
37
|
-
<path fill="#8b66ed" d="m174.997 1246.716 7.411-.263 33.83 61.32-13.08-3.837Z"/>
|
|
38
|
-
<path fill="#2cc5d2" d="m153.112 1247.492 11.16-.395.014.383 24.421 43.544-11.98-1.728Z"/>
|
|
39
|
-
<path fill="#fc4962" d="M125.227 1224.19v-8.878l30.211-4.464 17.92 8.545-41.396 1.436 15.11 11.82Z"/>
|
|
40
|
-
<path fill="#fe475f" d="M265.451 1292.508v18.84l-.092.043-16.939-30.08 2.927-15.799.214.206Z"/>
|
|
41
|
-
<path fill="#2d43cd" d="M125.227 1215.312v-18.871l30.211 14.407Z"/>
|
|
42
|
-
<path fill="#2ac4d1" d="M260.143 1379.764a12.5 12.5 0 0 1-3.03 2.099l-40.875-74.09 3.969 1.165Z"/>
|
|
43
|
-
<path fill="#fe475f" d="M265.426 1261.391q.025.677.025 1.346v16.241l-13.89-13.26-.186-.36-.028.154-9.53-9.099Z"/>
|
|
44
|
-
<path fill="#40b6d9" d="M265.451 1278.978v13.53c-4.95-9.535-12.604-24.31-13.89-26.79Z"/>
|
|
45
|
-
<path fill="#013afd" d="M125.227 1248.482v-7.892l18.12 7.249Z"/>
|
|
46
|
-
<path fill="#fe9197" d="M143.217 1163.611c2.517.515 5.203 1.558 7.99 3.168l48.434 27.963Z"/>
|
|
47
|
-
<path fill="#fec404" d="m251.347 1265.512.028-.154.186.36Z"/>
|
|
48
|
-
<path fill="#ccc" d="M265.451 1311.347v.207l-.092-.163Z"/>
|
|
49
|
-
<path fill="#2fc7d5" d="M383.274 1164.17a31 31 0 0 1-3.887 2.64l-2.59 1.495-95.242 18.988-50.727-18.022 25.55-32.892 63.642 26.919-61.075-31.394Z"/>
|
|
50
|
-
<path fill="#320fdd" d="m215.866 1112.967 18.535-16.15 5.766 32.704 16.212 6.858-25.551 32.892-57.218-19.484Z"/>
|
|
51
|
-
<path fill="#fd4a61" d="m305.806 1094.328 73.58 42.482c7.16 4.134 10.748 9.55 10.757 14.968l-40.264-5.624-25.491-7.867-33.1-30.491-18.842-4.065Z"/>
|
|
52
|
-
<path fill="#fec504" d="M388.516 1158.077c-1.145 2.145-2.892 4.205-5.242 6.093l-124.33-32.266 6.47-29.689 25.874 5.58 33.1 30.492Z"/>
|
|
53
|
-
<path fill="#320fdd" d="m179.744 1183.464-26.935-15.551 18.147-15.813 55.184 23.206-15.732 20.252Z"/>
|
|
54
|
-
<path fill="#33b6d9" d="m358.403 1178.925-52.998 30.599-44.794-17.995 89.572-18.117Z"/>
|
|
55
|
-
<path fill="#fd4a63" d="m234.401 1096.817 12.723-11.087 19.395 11.42-7.574 34.754 61.075 31.394-79.853-33.777Z"/>
|
|
56
|
-
<path fill="#fe9197" d="m152.809 1167.913-1.91-1.103c-10.42-6.016-13.273-14.747-8.558-22.25l56.907-27.791 15.854-3.136Z"/>
|
|
57
|
-
<path fill="#8b69f1" d="m305.405 1209.524-14.281 8.245c-5.252 3.032-11.697 4.954-18.462 5.767l-43.574-25.631 31.523-6.376Z"/>
|
|
58
|
-
<path fill="#3015d8" d="M252.294 1080.973c13.06-2.81 28.331-1.183 38.83 4.879l14.682 8.476-33.36 9.403-7.031-1.516 1.104-5.064-19.395-11.42Z"/>
|
|
59
|
-
<path fill="#2c0ace" d="M239.162 1085.852c3.85-2.223 8.342-3.85 13.132-4.88l-37.192 32.66-15.854 3.137-56.907 27.791c1.777-2.828 4.63-5.482 8.559-7.75Z"/>
|
|
60
|
-
<path fill="#2fc7d5" d="m229.088 1197.905-8.773 1.774-9.907-4.121 15.732-20.252 37.462 15.618Z"/>
|
|
61
|
-
<path fill="#33b6d9" d="M272.662 1223.536c-11.721 1.407-24.402-.515-33.5-5.767l-28.093-16.22 18.019-3.644Z"/>
|
|
62
|
-
<path fill="#fe9197" d="m264.078 1190.828-.476.096-37.462-15.618 4.688-6.035 50.727 18.022ZM170.956 1152.1l2.654-2.313 57.218 19.484-4.688 6.035Z"/>
|
|
63
|
-
<path fill="#30c3d9" d="M390.143 1151.778c.01 2.125-.533 4.25-1.627 6.3l-38.637-11.924Z"/>
|
|
64
|
-
<path fill="#ff4c60" d="m211.07 1201.55-31.326-18.086 40.57 16.215Z"/>
|
|
65
|
-
<path fill="#2d08d0" d="m376.798 1168.305-18.395 10.62-8.22-5.513Z"/>
|
|
66
|
-
<path fill="#2fc7d5" d="M264.887 1262.737c0-16.568 11.632-36.715 25.98-45l40.425-23.339 11.932 35.4-25.718 46.274-26.284-2.855-1.382.145-1.411-.146-23.542.675Z"/>
|
|
67
|
-
<path fill="#32abd6" d="M383.486 1164.684c2.376-.909 4.642-1.382 6.752-1.446l-7.859 43.045-.292 5.18 8.911 13.519 5.344-41.78 4.102-15.889c2.942 3.164 4.667 8.084 4.667 14.466v91.585h-1.324c-1.104 3.308-34.555 60.887-34.486 61.007l-21.13 12.2 8.305-32.902-10.115 14.693 10.115-20.342 47.315-78.499-7.45 6.694-15.674 18.243 2.62-89.698.155-.059Z"/>
|
|
68
|
-
<path fill="#2eabd6" d="m324.54 1360.214-33.672 19.44c-13.695 7.907-24.915 2.15-25.91-12.796l8.385-12.077 47.855-71.354 7.12-26.81 5.119-9.209.57 31.212-13.987 50.442 6.42-5.847-2.408 22.126.459 8.254Z"/>
|
|
69
|
-
<path fill="#8b65f0" d="M264.959 1366.858a33 33 0 0 1-.072-2.204v-88.582l24.953-2.71 26.235 2.554 1.43.156 10.813-19.454-7.12 26.81-47.855 71.353Z"/>
|
|
70
|
-
<path fill="#fd4861" d="m378.764 1192.394 3.89-5.923c-1.987-.058-4.42 64.146-4.402 65.055.019.91-5.901 22.365-5.901 22.365l-17.658 29.778-14.983 14.696 9.72-45.149.036-1.115 6.455-7.894Z"/>
|
|
71
|
-
<path fill="#32abd6" d="m377.832 1167.529.229-.133.703 24.998-22.843 71.813-6.455 7.894.572-17.63-3.443-30.74Z"/>
|
|
72
|
-
<path fill="#8c66ef" d="m328.775 1297.488.034-.126 20.657-25.261-.036 1.115-9.72 45.15 14.983-14.697-13.8 22.045-16.86 19.627 2.407-22.126-6.42 5.847Z"/>
|
|
73
|
-
<path fill="#2c08d3" d="m331.292 1194.398 27.203-15.705 5.51 13.714-20.781 37.391Z"/>
|
|
74
|
-
<path fill="#300bd2" d="m324.491 1353.595-.459-8.254 16.861-19.627 13.8-22.045 17.658-29.778 20.08-31.948 3.91-5.728 7.45-6.694-47.315 78.5-10.115 20.341-21.821 31.852Z"/>
|
|
75
|
-
<path fill="#2c08d3" d="m333.437 1247.408 13.158-23.676 3.443 30.738-4.056 14.678-14.316 17.914 2.34-8.442Z"/>
|
|
76
|
-
<path fill="#8b68ef" d="M405.111 1273.364v10.332c0 16.568-11.632 36.715-25.98 45l-9.83 5.675c-.07-.12 33.382-57.7 34.486-61.007Z"/>
|
|
77
|
-
<path fill="#fec504" d="M390.238 1163.238c4.109-.125 7.625 1.3 10.206 4.075l-4.102 15.89-5.344 41.779-8.91-13.518.291-5.18Z"/>
|
|
78
|
-
<path fill="#300bd2" d="m348.171 1346.57-23.631 13.644 31.936-46.545Z"/>
|
|
79
|
-
<path fill="#348fd8" d="M372.35 1273.891s5.921-21.455 5.902-22.365 2.415-65.113 4.402-65.055l-1.987 67.987 15.675-18.243-3.91 5.728Z"/>
|
|
80
|
-
<path fill="#32abd6" d="m358.495 1178.693 19.337-11.164-13.827 24.878Z"/>
|
|
81
|
-
<path fill="#fd4861" d="m331.391 1288.05.275-.988 14.316-17.914 4.056-14.678-.572 17.63-20.657 25.262Z"/>
|
|
82
|
-
<path fill="#ccc" fillOpacity=".579" d="m378.06 1167.396 1.07-.617a31 31 0 0 1 4.158-2.019l-.634 21.71-3.89 5.924Z"/>
|
|
83
|
-
<path fill="#2fc7d5" d="M264.887 1276.072v-2.18l23.542-.676 1.411.146Z"/>
|
|
84
|
-
<path fill="#8b65f0" d="m291.193 1273.502-1.353-.14 1.382-.145 24.853 2.7Z"/>
|
|
85
|
-
</svg>
|
|
86
|
-
);
|
|
87
|
-
|
|
88
|
-
}
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
import React, { createContext, useCallback, useContext, useMemo, useState } from "react";
|
|
2
|
-
import { Alert, Typography } from "@rebasepro/ui";
|
|
3
|
-
|
|
4
|
-
// ── Schema Drift Context ────────────────────────────────────────────────
|
|
5
|
-
|
|
6
|
-
interface SchemaDriftContextValue {
|
|
7
|
-
/** Report a schema drift error (call from any data-loading hook). */
|
|
8
|
-
reportSchemaDrift: (message: string) => void;
|
|
9
|
-
/** The most recent schema drift message, or null. */
|
|
10
|
-
schemaDriftMessage: string | null;
|
|
11
|
-
/** Dismiss the banner for this session. */
|
|
12
|
-
dismiss: () => void;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const SchemaDriftContext = createContext<SchemaDriftContextValue>({
|
|
16
|
-
reportSchemaDrift: () => {},
|
|
17
|
-
schemaDriftMessage: null,
|
|
18
|
-
dismiss: () => {}
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
export function useSchemaDriftContext(): SchemaDriftContextValue {
|
|
22
|
-
return useContext(SchemaDriftContext);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Detects schema drift from an error object.
|
|
27
|
-
* Checks both the error code (from `RebaseApiError`) and the message
|
|
28
|
-
* for schema drift indicators.
|
|
29
|
-
*/
|
|
30
|
-
export function isSchemaDriftError(error: unknown): boolean {
|
|
31
|
-
if (!error || typeof error !== "object") return false;
|
|
32
|
-
// RebaseApiError from the SDK has a `code` property
|
|
33
|
-
if ("code" in error && (error as { code?: string }).code === "SCHEMA_DRIFT") {
|
|
34
|
-
return true;
|
|
35
|
-
}
|
|
36
|
-
// Fallback: check the message for the telltale phrase
|
|
37
|
-
if (error instanceof Error && error.message.includes("Schema drift:")) {
|
|
38
|
-
return true;
|
|
39
|
-
}
|
|
40
|
-
return false;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// ── Provider ─────────────────────────────────────────────────────────────
|
|
44
|
-
|
|
45
|
-
export function SchemaDriftProvider({ children }: { children: React.ReactNode }) {
|
|
46
|
-
const [schemaDriftMessage, setSchemaDriftMessage] = useState<string | null>(null);
|
|
47
|
-
const [dismissed, setDismissed] = useState(false);
|
|
48
|
-
|
|
49
|
-
const reportSchemaDrift = useCallback((message: string) => {
|
|
50
|
-
setSchemaDriftMessage(prev => prev ?? message);
|
|
51
|
-
}, []);
|
|
52
|
-
|
|
53
|
-
const dismiss = useCallback(() => {
|
|
54
|
-
setDismissed(true);
|
|
55
|
-
}, []);
|
|
56
|
-
|
|
57
|
-
const value = useMemo<SchemaDriftContextValue>(() => ({
|
|
58
|
-
reportSchemaDrift,
|
|
59
|
-
schemaDriftMessage: dismissed ? null : schemaDriftMessage,
|
|
60
|
-
dismiss
|
|
61
|
-
}), [reportSchemaDrift, schemaDriftMessage, dismissed, dismiss]);
|
|
62
|
-
|
|
63
|
-
return (
|
|
64
|
-
<SchemaDriftContext.Provider value={value}>
|
|
65
|
-
{children}
|
|
66
|
-
</SchemaDriftContext.Provider>
|
|
67
|
-
);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// ── Banner Component ─────────────────────────────────────────────────────
|
|
71
|
-
|
|
72
|
-
export interface SchemaDriftBannerProps {
|
|
73
|
-
className?: string;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Persistent banner shown when a schema drift error is detected.
|
|
78
|
-
*/
|
|
79
|
-
export function SchemaDriftBanner({ className }: SchemaDriftBannerProps) {
|
|
80
|
-
const { schemaDriftMessage, dismiss } = useSchemaDriftContext();
|
|
81
|
-
|
|
82
|
-
if (!schemaDriftMessage) return null;
|
|
83
|
-
|
|
84
|
-
return (
|
|
85
|
-
<Alert
|
|
86
|
-
color="warning"
|
|
87
|
-
size="small"
|
|
88
|
-
onDismiss={dismiss}
|
|
89
|
-
className={className}
|
|
90
|
-
>
|
|
91
|
-
<div className="flex flex-col gap-1">
|
|
92
|
-
<Typography variant="label" className="text-amber-800 dark:text-amber-200">
|
|
93
|
-
Schema drift detected
|
|
94
|
-
</Typography>
|
|
95
|
-
<Typography variant="body2" className="text-amber-700 dark:text-amber-300">
|
|
96
|
-
{schemaDriftMessage}{" "}
|
|
97
|
-
Run <code className="bg-amber-200 dark:bg-amber-800 px-1 rounded text-xs">pnpm db:push</code> in your backend terminal to sync.
|
|
98
|
-
</Typography>
|
|
99
|
-
</div>
|
|
100
|
-
</Alert>
|
|
101
|
-
);
|
|
102
|
-
}
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import { Button, Dialog, DialogActions, DialogContent, DialogTitle, Typography } from "@rebasepro/ui";
|
|
3
|
-
import { useTranslation } from "../hooks/useTranslation";
|
|
4
|
-
|
|
5
|
-
export interface UnsavedChangesDialogProps {
|
|
6
|
-
open: boolean;
|
|
7
|
-
body?: React.ReactNode;
|
|
8
|
-
title?: string;
|
|
9
|
-
handleOk: () => void;
|
|
10
|
-
handleCancel: () => void;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export function UnsavedChangesDialog({
|
|
14
|
-
open,
|
|
15
|
-
handleOk,
|
|
16
|
-
handleCancel,
|
|
17
|
-
body,
|
|
18
|
-
title
|
|
19
|
-
}: UnsavedChangesDialogProps) {
|
|
20
|
-
|
|
21
|
-
const { t } = useTranslation();
|
|
22
|
-
|
|
23
|
-
return (
|
|
24
|
-
<Dialog
|
|
25
|
-
onEscapeKeyDown={() => {
|
|
26
|
-
handleCancel();
|
|
27
|
-
}}
|
|
28
|
-
open={open}
|
|
29
|
-
>
|
|
30
|
-
<DialogTitle variant={"h6"}>{title}</DialogTitle>
|
|
31
|
-
<DialogContent>
|
|
32
|
-
|
|
33
|
-
<Typography>
|
|
34
|
-
{body ?? t("unsaved_changes")}
|
|
35
|
-
</Typography>
|
|
36
|
-
|
|
37
|
-
</DialogContent>
|
|
38
|
-
<DialogActions>
|
|
39
|
-
<Button variant="text"
|
|
40
|
-
onClick={handleCancel} autoFocus> {t("cancel")} </Button>
|
|
41
|
-
<Button
|
|
42
|
-
onClick={handleOk}> {t("ok")} </Button>
|
|
43
|
-
</DialogActions>
|
|
44
|
-
</Dialog>
|
|
45
|
-
);
|
|
46
|
-
}
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import { CircleUserIcon, cls, defaultBorderMixin } from "@rebasepro/ui";
|
|
2
|
-
import { User } from "@rebasepro/types";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Component to render a single user with name and email
|
|
6
|
-
*/
|
|
7
|
-
export function UserDisplay({
|
|
8
|
-
user
|
|
9
|
-
}: { user: User | null }) {
|
|
10
|
-
if (!user) {
|
|
11
|
-
return <span className={cls("italic", "text-text-disabled dark:text-text-disabled-dark", "text-sm")}>Not set</span>;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const avatarSizeClass = "w-6 h-6";
|
|
15
|
-
|
|
16
|
-
return (
|
|
17
|
-
<div className={cls(
|
|
18
|
-
"inline-flex items-center gap-4 px-2 py-1 rounded-xl text-left max-w-full overflow-hidden",
|
|
19
|
-
"bg-surface-accent-100 dark:bg-surface-accent-800",
|
|
20
|
-
"border",
|
|
21
|
-
defaultBorderMixin
|
|
22
|
-
)}>
|
|
23
|
-
{user.photoURL ? (
|
|
24
|
-
<img
|
|
25
|
-
src={user.photoURL}
|
|
26
|
-
alt={user.displayName || user.email || "User"}
|
|
27
|
-
className={cls(
|
|
28
|
-
"rounded-full object-cover",
|
|
29
|
-
avatarSizeClass
|
|
30
|
-
)}
|
|
31
|
-
/>
|
|
32
|
-
) : (
|
|
33
|
-
<CircleUserIcon
|
|
34
|
-
className={cls(
|
|
35
|
-
"text-text-secondary dark:text-text-secondary-dark",
|
|
36
|
-
avatarSizeClass
|
|
37
|
-
)}
|
|
38
|
-
/>
|
|
39
|
-
)}
|
|
40
|
-
<div className="flex flex-col min-w-0">
|
|
41
|
-
<span className={cls("font-regular truncate", "text-sm")}>
|
|
42
|
-
{user.displayName || user.email || "-"}
|
|
43
|
-
</span>
|
|
44
|
-
{user.displayName && user.email && (
|
|
45
|
-
<span className={cls("text-text-secondary dark:text-text-secondary-dark truncate",
|
|
46
|
-
"text-xs"
|
|
47
|
-
)}>
|
|
48
|
-
{user.email}
|
|
49
|
-
</span>
|
|
50
|
-
)}
|
|
51
|
-
</div>
|
|
52
|
-
</div>
|
|
53
|
-
);
|
|
54
|
-
}
|