@proveanything/smartlinks-auth-ui 0.3.10 → 0.3.12

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.
@@ -1 +1 @@
1
- {"version":3,"file":"AccountManagement.d.ts","sourceRoot":"","sources":["../../src/components/AccountManagement.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA2C,MAAM,OAAO,CAAC;AAMhE,OAAO,KAAK,EAAE,sBAAsB,EAAe,MAAM,UAAU,CAAC;AACpE,OAAO,qBAAqB,CAAC;AAO7B,eAAO,MAAM,iBAAiB,EAAE,KAAK,CAAC,EAAE,CAAC,sBAAsB,CA0vB9D,CAAC"}
1
+ {"version":3,"file":"AccountManagement.d.ts","sourceRoot":"","sources":["../../src/components/AccountManagement.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA2C,MAAM,OAAO,CAAC;AAMhE,OAAO,KAAK,EAAE,sBAAsB,EAAe,MAAM,UAAU,CAAC;AACpE,OAAO,qBAAqB,CAAC;AAO7B,eAAO,MAAM,iBAAiB,EAAE,KAAK,CAAC,EAAE,CAAC,sBAAsB,CA0wB9D,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"SmartlinksAuthUI.d.ts","sourceRoot":"","sources":["../../src/components/SmartlinksAuthUI.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAuC,MAAM,OAAO,CAAC;AAY5D,OAAO,KAAK,EAAE,qBAAqB,EAAyF,MAAM,UAAU,CAAC;AAkH7I,QAAA,MAAM,mBAAmB,QAAa,OAAO,CAAC,IAAI,CAmCjD,CAAC;AAwEF,OAAO,EAAE,mBAAmB,EAAE,CAAC;AAK/B,eAAO,MAAM,gBAAgB,EAAE,KAAK,CAAC,EAAE,CAAC,qBAAqB,CAyhD5D,CAAC"}
1
+ {"version":3,"file":"SmartlinksAuthUI.d.ts","sourceRoot":"","sources":["../../src/components/SmartlinksAuthUI.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAuC,MAAM,OAAO,CAAC;AAY5D,OAAO,KAAK,EAAE,qBAAqB,EAAyF,MAAM,UAAU,CAAC;AA0I7I,QAAA,MAAM,mBAAmB,QAAa,OAAO,CAAC,IAAI,CAmCjD,CAAC;AAwEF,OAAO,EAAE,mBAAmB,EAAE,CAAC;AAK/B,eAAO,MAAM,gBAAgB,EAAE,KAAK,CAAC,EAAE,CAAC,qBAAqB,CA0mD5D,CAAC"}
@@ -1,35 +1,5 @@
1
1
  import React from 'react';
2
- import type { ContactResponse } from '@proveanything/smartlinks';
3
- import type { AuthUser, AuthStateChangeCallback, AuthProviderProps } from '../types';
4
- interface AuthContextValue {
5
- user: AuthUser | null;
6
- token: string | null;
7
- accountData: Record<string, any> | null;
8
- accountInfo: Record<string, any> | null;
9
- isAuthenticated: boolean;
10
- isVerified: boolean;
11
- isLoading: boolean;
12
- isOnline: boolean;
13
- proxyMode: boolean;
14
- contact: ContactResponse | null;
15
- contactId: string | null;
16
- getContact: (forceRefresh?: boolean) => Promise<ContactResponse | null>;
17
- updateContactCustomFields: (customFields: Record<string, any>) => Promise<ContactResponse>;
18
- login: (token: string, user: AuthUser, accountData?: Record<string, any>, isNewUser?: boolean, expiresAt?: number) => Promise<void>;
19
- logout: () => Promise<void>;
20
- getToken: () => Promise<string | null>;
21
- getTokenInfo: () => Promise<{
22
- token: string;
23
- expiresAt: number;
24
- expiresIn: number;
25
- } | null>;
26
- refreshToken: () => Promise<string>;
27
- getAccount: (forceRefresh?: boolean) => Promise<Record<string, any>>;
28
- refreshAccount: () => Promise<Record<string, any>>;
29
- clearAccountCache: () => Promise<void>;
30
- onAuthStateChange: (callback: AuthStateChangeCallback) => () => void;
31
- retryVerification: () => Promise<boolean>;
32
- }
2
+ import type { AuthProviderProps, AuthContextValue } from '../types';
33
3
  export declare const AuthContext: React.Context<AuthContextValue | undefined>;
34
4
  export type { AuthContextValue };
35
5
  export declare const AuthProvider: React.FC<AuthProviderProps>;
@@ -1 +1 @@
1
- {"version":3,"file":"AuthContext.d.ts","sourceRoot":"","sources":["../../src/context/AuthContext.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8E,MAAM,OAAO,CAAC;AAGnG,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAGjE,OAAO,KAAK,EAAE,QAAQ,EAAE,uBAAuB,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAErF,UAAU,gBAAgB;IACxB,IAAI,EAAE,QAAQ,GAAG,IAAI,CAAC;IACtB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,CAAC;IACxC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,CAAC;IACxC,eAAe,EAAE,OAAO,CAAC;IACzB,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;IAClB,SAAS,EAAE,OAAO,CAAC;IAGnB,OAAO,EAAE,eAAe,GAAG,IAAI,CAAC;IAChC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,UAAU,EAAE,CAAC,YAAY,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAAC;IACxE,yBAAyB,EAAE,CAAC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,OAAO,CAAC,eAAe,CAAC,CAAC;IAG3F,KAAK,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,SAAS,CAAC,EAAE,OAAO,EAAE,SAAS,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpI,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,QAAQ,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACvC,YAAY,EAAE,MAAM,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC,CAAC;IAC5F,YAAY,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IACpC,UAAU,EAAE,CAAC,YAAY,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC;IACrE,cAAc,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC;IACnD,iBAAiB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,iBAAiB,EAAE,CAAC,QAAQ,EAAE,uBAAuB,KAAK,MAAM,IAAI,CAAC;IACrE,iBAAiB,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;CAC3C;AAGD,eAAO,MAAM,WAAW,6CAAyD,CAAC;AAGlF,YAAY,EAAE,gBAAgB,EAAE,CAAC;AAEjC,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CA0yBpD,CAAC;AAEF,eAAO,MAAM,OAAO,QAAO,gBAM1B,CAAC"}
1
+ {"version":3,"file":"AuthContext.d.ts","sourceRoot":"","sources":["../../src/context/AuthContext.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8E,MAAM,OAAO,CAAC;AAOnG,OAAO,KAAK,EAAqC,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAGvG,eAAO,MAAM,WAAW,6CAAyD,CAAC;AAGlF,YAAY,EAAE,gBAAgB,EAAE,CAAC;AAEjC,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CA+0BpD,CAAC;AAEF,eAAO,MAAM,OAAO,QAAO,gBAM1B,CAAC"}
package/dist/index.esm.js CHANGED
@@ -2,7 +2,7 @@ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
2
  import React, { useEffect, useState, useMemo, useRef, useCallback, createContext, useContext } from 'react';
3
3
  import * as smartlinks from '@proveanything/smartlinks';
4
4
  import { iframe, SmartlinksApiError } from '@proveanything/smartlinks';
5
- import { post } from '@proveanything/smartlinks/dist/http';
5
+ import { post, getApiHeaders, isProxyEnabled } from '@proveanything/smartlinks/dist/http';
6
6
 
7
7
  const AuthContainer = ({ children, theme = 'light', className = '', config, minimal = false, }) => {
8
8
  // Apply CSS variables for customization
@@ -10849,7 +10849,7 @@ class AuthAPI {
10849
10849
  });
10850
10850
  // Exchange authorization code for tokens via backend
10851
10851
  // Use direct HTTP call since SDK may not have this method in authKit namespace yet
10852
- return post(`/api/v1/authkit/${this.clientId}/google-code`, {
10852
+ return post(`/authkit/${this.clientId}/google-code`, {
10853
10853
  code,
10854
10854
  redirectUri,
10855
10855
  });
@@ -11530,21 +11530,48 @@ collectionId, enableContactSync, enableInteractionTracking, interactionAppId, in
11530
11530
  return null;
11531
11531
  }
11532
11532
  }, [collectionId, shouldSyncContacts, proxyMode, token, accountData, accountInfo, isVerified, notifyAuthStateChange]);
11533
+ // Detect legacy vs new interactionConfig shape
11534
+ const isLegacyConfig = interactionConfig && !('interactionId' in interactionConfig) && ('login' in interactionConfig || 'logout' in interactionConfig || 'signup' in interactionConfig);
11535
+ if (isLegacyConfig) {
11536
+ console.warn('[AuthContext] Deprecated: interactionConfig uses legacy per-event-type IDs. Migrate to { interactionId, scopes? } shape. Legacy support will be removed in a future release.');
11537
+ }
11533
11538
  // Track interaction event (non-blocking)
11534
- // IMPORTANT: Only tracks if an explicit interaction ID is configured for the event type.
11535
- // Interaction IDs must be pre-created in SmartLinks - you cannot use arbitrary IDs.
11539
+ // Uses scope-based architecture: single interactionId + scope field to differentiate events.
11536
11540
  const trackInteraction = useCallback(async (eventType, userId, currentContactId, metadata) => {
11537
11541
  if (!collectionId || !shouldTrackInteractions)
11538
11542
  return;
11539
- const interactionIdMap = {
11540
- login: interactionConfig?.login,
11541
- logout: interactionConfig?.logout,
11542
- signup: interactionConfig?.signup,
11543
- session_restore: interactionConfig?.sessionRestore,
11544
- };
11545
- const interactionId = interactionIdMap[eventType];
11546
- if (!interactionId)
11543
+ let interactionId;
11544
+ let scope;
11545
+ if (isLegacyConfig) {
11546
+ // Legacy: per-event-type IDs (deprecated)
11547
+ const legacyConfig = interactionConfig;
11548
+ const legacyMap = {
11549
+ login: legacyConfig?.login,
11550
+ logout: legacyConfig?.logout,
11551
+ signup: legacyConfig?.signup,
11552
+ session_restore: legacyConfig?.sessionRestore,
11553
+ };
11554
+ interactionId = legacyMap[eventType];
11555
+ if (!interactionId)
11556
+ return;
11557
+ }
11558
+ else if (interactionConfig && 'interactionId' in interactionConfig) {
11559
+ // New: single ID + scope
11560
+ interactionId = interactionConfig.interactionId;
11561
+ if (!interactionId)
11562
+ return;
11563
+ const defaultScopes = {
11564
+ login: 'login',
11565
+ logout: 'logout',
11566
+ signup: 'signup',
11567
+ session_restore: 'session_restore',
11568
+ };
11569
+ const customScopes = interactionConfig.scopes || {};
11570
+ scope = customScopes[eventType === 'session_restore' ? 'sessionRestore' : eventType] || defaultScopes[eventType];
11571
+ }
11572
+ else {
11547
11573
  return;
11574
+ }
11548
11575
  try {
11549
11576
  await smartlinks.interactions.submitPublicEvent(collectionId, {
11550
11577
  collectionId,
@@ -11552,7 +11579,7 @@ collectionId, enableContactSync, enableInteractionTracking, interactionAppId, in
11552
11579
  userId,
11553
11580
  contactId: currentContactId || undefined,
11554
11581
  appId: interactionAppId,
11555
- eventType,
11582
+ scope,
11556
11583
  outcome: 'completed',
11557
11584
  metadata: {
11558
11585
  ...metadata,
@@ -11565,7 +11592,7 @@ collectionId, enableContactSync, enableInteractionTracking, interactionAppId, in
11565
11592
  catch (err) {
11566
11593
  // Non-blocking
11567
11594
  }
11568
- }, [collectionId, shouldTrackInteractions, interactionAppId, interactionConfig, user, token, accountData, accountInfo, isVerified, contact, contactId, notifyAuthStateChange]);
11595
+ }, [collectionId, shouldTrackInteractions, interactionAppId, interactionConfig, isLegacyConfig, user, token, accountData, accountInfo, isVerified, contact, contactId, notifyAuthStateChange]);
11569
11596
  // Keep refs in sync with latest callbacks (avoids stale closures)
11570
11597
  useEffect(() => {
11571
11598
  syncContactRef.current = syncContact;
@@ -11613,32 +11640,42 @@ collectionId, enableContactSync, enableInteractionTracking, interactionAppId, in
11613
11640
  const initializeAuth = async () => {
11614
11641
  try {
11615
11642
  if (proxyMode) {
11616
- try {
11617
- const accountResponse = await smartlinks.auth.getAccount();
11618
- const accountAny = accountResponse;
11619
- const hasValidSession = accountAny?.uid && accountAny.uid.length > 0;
11620
- if (hasValidSession && isMounted) {
11621
- const userFromAccount = {
11622
- uid: accountAny.uid,
11623
- email: accountAny?.email,
11624
- displayName: accountAny?.displayName || accountAny?.name,
11625
- phoneNumber: accountAny?.phoneNumber,
11626
- };
11627
- setUser(userFromAccount);
11628
- setAccountData(accountResponse);
11629
- setAccountInfo(accountResponse);
11630
- setIsVerified(true);
11631
- notifyAuthStateChange('LOGIN', userFromAccount, null, accountResponse, accountResponse, true);
11632
- // Sync contact in background (proxy mode) - use ref for stable dependency
11633
- syncContactRef.current?.(userFromAccount, accountResponse);
11643
+ // Check if credentials exist before making the API call
11644
+ const headers = getApiHeaders();
11645
+ const hasBearer = !!headers['Authorization'];
11646
+ const hasSdkProxy = isProxyEnabled();
11647
+ if (!hasBearer && !hasSdkProxy) {
11648
+ console.debug('[AuthContext] Skipping getAccount - no credentials available');
11649
+ // Fall through to "no valid session" state
11650
+ }
11651
+ else {
11652
+ try {
11653
+ const accountResponse = await smartlinks.auth.getAccount();
11654
+ const accountAny = accountResponse;
11655
+ const hasValidSession = accountAny?.uid && accountAny.uid.length > 0;
11656
+ if (hasValidSession && isMounted) {
11657
+ const userFromAccount = {
11658
+ uid: accountAny.uid,
11659
+ email: accountAny?.email,
11660
+ displayName: accountAny?.displayName || accountAny?.name,
11661
+ phoneNumber: accountAny?.phoneNumber,
11662
+ };
11663
+ setUser(userFromAccount);
11664
+ setAccountData(accountResponse);
11665
+ setAccountInfo(accountResponse);
11666
+ setIsVerified(true);
11667
+ notifyAuthStateChange('LOGIN', userFromAccount, null, accountResponse, accountResponse, true);
11668
+ // Sync contact in background (proxy mode) - use ref for stable dependency
11669
+ syncContactRef.current?.(userFromAccount, accountResponse);
11670
+ }
11671
+ else if (isMounted) {
11672
+ // No valid session, awaiting login
11673
+ }
11634
11674
  }
11635
- else if (isMounted) {
11636
- // No valid session, awaiting login
11675
+ catch (error) {
11676
+ // auth.getAccount() failed, awaiting login
11637
11677
  }
11638
- }
11639
- catch (error) {
11640
- // auth.getAccount() failed, awaiting login
11641
- }
11678
+ } // end else (has credentials)
11642
11679
  if (isMounted) {
11643
11680
  setIsLoading(false);
11644
11681
  initializingRef.current = false;
@@ -11670,7 +11707,7 @@ collectionId, enableContactSync, enableInteractionTracking, interactionAppId, in
11670
11707
  pendingVerificationRef.current = false;
11671
11708
  notifyAuthStateChange('SESSION_VERIFIED', storedUser, storedToken.token, storedAccountData || null, null, true, null, storedContactId);
11672
11709
  // Track session restore interaction (optional) - use ref for stable dependency
11673
- if (interactionConfig?.sessionRestore) {
11710
+ if (interactionConfig && ('interactionId' in interactionConfig || interactionConfig?.sessionRestore)) {
11674
11711
  trackInteractionRef.current?.('session_restore', storedUser.uid, storedContactId);
11675
11712
  }
11676
11713
  }
@@ -11716,7 +11753,7 @@ collectionId, enableContactSync, enableInteractionTracking, interactionAppId, in
11716
11753
  return () => {
11717
11754
  isMounted = false;
11718
11755
  };
11719
- }, [proxyMode, notifyAuthStateChange, isNetworkError, interactionConfig?.sessionRestore]);
11756
+ }, [proxyMode, notifyAuthStateChange, isNetworkError, interactionConfig]);
11720
11757
  // Listen for parent auth state changes (proxy mode only)
11721
11758
  useEffect(() => {
11722
11759
  if (!proxyMode)
@@ -12085,9 +12122,9 @@ collectionId, enableContactSync, enableInteractionTracking, interactionAppId, in
12085
12122
  if (!storedToken?.expiresAt)
12086
12123
  return;
12087
12124
  const now = Date.now();
12088
- const tokenLifetime = storedToken.expiresAt - (storedToken.expiresAt - (7 * 24 * 60 * 60 * 1000));
12089
- const tokenAge = now - (storedToken.expiresAt - (7 * 24 * 60 * 60 * 1000));
12090
- const percentUsed = (tokenAge / tokenLifetime) * 100;
12125
+ const remainingMs = storedToken.expiresAt - now;
12126
+ const totalLifetimeMs = 7 * 24 * 60 * 60 * 1000;
12127
+ const percentUsed = totalLifetimeMs > 0 ? ((totalLifetimeMs - remainingMs) / totalLifetimeMs) * 100 : 100;
12091
12128
  if (percentUsed >= refreshThresholdPercent) {
12092
12129
  try {
12093
12130
  await refreshToken();
@@ -12283,6 +12320,25 @@ const getExpirationFromResponse = (response) => {
12283
12320
  };
12284
12321
  // Default Smartlinks Google OAuth Client ID (public - safe to expose)
12285
12322
  const DEFAULT_GOOGLE_CLIENT_ID = '696509063554-jdlbjl8vsjt7cr0vgkjkjf3ffnvi3a70.apps.googleusercontent.com';
12323
+ // Default Google OAuth proxy URL (hosted on our whitelisted domain)
12324
+ const DEFAULT_GOOGLE_PROXY_URL = 'https://smartlinks-auth-kit.lovable.app/google-proxy.html';
12325
+ // Exact hostnames where Google OAuth is registered and inline/OneTap flow works directly.
12326
+ // Only specific registered origins — NOT broad wildcards like *.lovable.app
12327
+ const WHITELISTED_GOOGLE_OAUTH_HOSTS = [
12328
+ 'smartlinks-auth-kit.lovable.app', // This app's dev/preview domain (registered in Google Console)
12329
+ 'smartlinks.app', // Production root
12330
+ 'localhost', // Local dev
12331
+ '127.0.0.1', // Local dev
12332
+ ];
12333
+ /**
12334
+ * Check if the current domain is whitelisted for direct Google OAuth.
12335
+ * Uses exact hostname match (plus subdomain match for smartlinks.app production).
12336
+ * Returns true if OneTap/inline flow can work without a proxy.
12337
+ */
12338
+ const isWhitelistedGoogleDomain = () => {
12339
+ const hostname = window.location.hostname;
12340
+ return WHITELISTED_GOOGLE_OAUTH_HOSTS.some(domain => hostname === domain || hostname.endsWith(`.${domain}`));
12341
+ };
12286
12342
  // Default auth UI configuration when no clientId is provided
12287
12343
  const DEFAULT_AUTH_CONFIG = {
12288
12344
  branding: {
@@ -13051,16 +13107,29 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
13051
13107
  }
13052
13108
  };
13053
13109
  const handleGoogleLogin = async () => {
13110
+ const hasCustomGoogleClientId = !!config?.googleClientId;
13054
13111
  const googleClientId = config?.googleClientId || DEFAULT_GOOGLE_CLIENT_ID;
13055
13112
  const configuredFlow = config?.googleOAuthFlow || 'oneTap';
13056
13113
  const isWebView = detectWebView();
13057
13114
  const nativeBridge = getNativeBridge();
13058
13115
  const oauthFlow = (configuredFlow === 'oneTap' && isWebView && !nativeBridge) ? 'redirect' : configuredFlow;
13116
+ // Auto-detect proxy need:
13117
+ // - If explicitly configured, use that URL
13118
+ // - If user has their own Google Client ID, they've registered their domains — no proxy needed
13119
+ // - If on a whitelisted SmartLinks domain, inline flow works directly
13120
+ // - Otherwise, auto-use the default proxy URL
13121
+ const isWhitelisted = isWhitelistedGoogleDomain();
13122
+ const googleProxyUrl = config?.googleOAuthProxyUrl
13123
+ || (!hasCustomGoogleClientId && !isWhitelisted ? DEFAULT_GOOGLE_PROXY_URL : undefined);
13059
13124
  log.log('Google Auth initiated:', {
13060
13125
  configuredFlow,
13061
13126
  effectiveFlow: oauthFlow,
13062
13127
  isWebView,
13063
13128
  hasNativeBridge: !!nativeBridge,
13129
+ hasCustomGoogleClientId,
13130
+ isWhitelistedDomain: isWhitelisted,
13131
+ usingProxy: !!googleProxyUrl,
13132
+ proxyUrl: googleProxyUrl,
13064
13133
  });
13065
13134
  setLoading(true);
13066
13135
  setError(undefined);
@@ -13135,6 +13204,70 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
13135
13204
  setLoading(false);
13136
13205
  return;
13137
13206
  }
13207
+ // Priority 3: Google OAuth Proxy popup (for custom domains not registered in Google Console)
13208
+ if (googleProxyUrl) {
13209
+ log.log('Using Google OAuth proxy popup:', googleProxyUrl);
13210
+ const proxyParams = new URLSearchParams({
13211
+ clientId,
13212
+ returnOrigin: window.location.origin,
13213
+ ...(apiEndpoint ? { apiEndpoint } : {}),
13214
+ });
13215
+ const popupUrl = `${googleProxyUrl}?${proxyParams.toString()}`;
13216
+ const popup = window.open(popupUrl, 'google-oauth-proxy', 'width=500,height=600,menubar=no,toolbar=no,location=yes');
13217
+ if (!popup) {
13218
+ setError('Popup blocked. Please allow popups for this site and try again.');
13219
+ setLoading(false);
13220
+ return;
13221
+ }
13222
+ // Listen for result from proxy popup
13223
+ const handleProxyMessage = async (event) => {
13224
+ // Validate origin matches proxy URL
13225
+ try {
13226
+ const proxyOrigin = new URL(googleProxyUrl).origin;
13227
+ if (event.origin !== proxyOrigin)
13228
+ return;
13229
+ }
13230
+ catch {
13231
+ return;
13232
+ }
13233
+ if (event.data?.type !== 'smartlinks:google-proxy:result')
13234
+ return;
13235
+ window.removeEventListener('message', handleProxyMessage);
13236
+ clearInterval(checkClosed);
13237
+ try {
13238
+ if (event.data.success && event.data.token) {
13239
+ const { token, user, accountData, isNewUser, expiresAt, expiresIn } = event.data;
13240
+ const expiration = expiresAt || (expiresIn ? Date.now() + expiresIn : undefined);
13241
+ await auth.login(token, user, accountData, isNewUser, expiration);
13242
+ setAuthSuccess(true);
13243
+ setSuccessMessage('Google login successful!');
13244
+ onAuthSuccess(token, user, accountData);
13245
+ }
13246
+ else {
13247
+ const errorMsg = event.data.error || 'Google login failed';
13248
+ setError(getFriendlyErrorMessage(errorMsg));
13249
+ onAuthError?.(new Error(errorMsg));
13250
+ }
13251
+ }
13252
+ catch (err) {
13253
+ setError(getFriendlyErrorMessage(err));
13254
+ onAuthError?.(err instanceof Error ? err : new Error(getFriendlyErrorMessage(err)));
13255
+ }
13256
+ finally {
13257
+ setLoading(false);
13258
+ }
13259
+ };
13260
+ window.addEventListener('message', handleProxyMessage);
13261
+ // Monitor popup close without result (user closed it)
13262
+ const checkClosed = setInterval(() => {
13263
+ if (popup.closed) {
13264
+ clearInterval(checkClosed);
13265
+ window.removeEventListener('message', handleProxyMessage);
13266
+ setLoading(false);
13267
+ }
13268
+ }, 500);
13269
+ return; // Don't set loading to false - waiting for popup
13270
+ }
13138
13271
  log.log('Using web-based OAuth flow:', oauthFlow);
13139
13272
  // Priority 3: Web-based flows (redirect, popup, oneTap)
13140
13273
  // Dynamically load Google Identity Services if not already loaded
@@ -13368,7 +13501,6 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
13368
13501
  if (resetToken && confirmPassword) {
13369
13502
  // Complete password reset with token
13370
13503
  await api.completePasswordReset(resetToken, emailOrPassword);
13371
- await api.completePasswordReset(resetToken, emailOrPassword);
13372
13504
  // Auto-login with the new password if we have the email
13373
13505
  if (resetEmail) {
13374
13506
  try {
@@ -13693,6 +13825,12 @@ const AccountManagement = ({ apiEndpoint, clientId, collectionId, onError, class
13693
13825
  const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
13694
13826
  const { showProfileSection = true, showEmailSection = true, showPasswordSection = true, showPhoneSection = true, showDeleteAccount = false, showCustomFields = true, // New: show schema-driven custom fields
13695
13827
  } = customization;
13828
+ // Provider awareness helpers
13829
+ // If providers is undefined (backend not updated yet), fall back to showing everything
13830
+ const providers = profile?.providers;
13831
+ const hasPassword = profile?.hasPassword ?? (providers === undefined ? undefined : providers?.includes('password'));
13832
+ const isGoogleOnly = providers !== undefined && providers.includes('google.com') && !providers.includes('password');
13833
+ const isPhoneOnly = providers !== undefined && providers.includes('phone') && providers.length === 1;
13696
13834
  // Reinitialize Smartlinks SDK when apiEndpoint changes
13697
13835
  useEffect(() => {
13698
13836
  if (apiEndpoint) {
@@ -13710,13 +13848,11 @@ const AccountManagement = ({ apiEndpoint, clientId, collectionId, onError, class
13710
13848
  const loadSchema = async () => {
13711
13849
  setSchemaLoading(true);
13712
13850
  try {
13713
- console.log('[AccountManagement] Loading schema for collection:', collectionId);
13714
13851
  const schemaResult = await smartlinks.contact.publicGetSchema(collectionId);
13715
- console.log('[AccountManagement] Schema loaded:', schemaResult);
13716
13852
  setSchema(schemaResult);
13717
13853
  }
13718
13854
  catch (err) {
13719
- console.warn('[AccountManagement] Failed to load schema:', err);
13855
+ // Non-fatal - component works without schema
13720
13856
  // Non-fatal - component works without schema
13721
13857
  }
13722
13858
  finally {
@@ -13731,7 +13867,6 @@ const AccountManagement = ({ apiEndpoint, clientId, collectionId, onError, class
13731
13867
  // where getContact() is called before the server knows about the user.
13732
13868
  useEffect(() => {
13733
13869
  if (!auth.isAuthenticated || !auth.isVerified) {
13734
- console.log('[AccountManagement] Waiting for verified authentication before loading profile...');
13735
13870
  return;
13736
13871
  }
13737
13872
  loadProfile();
@@ -13832,7 +13967,7 @@ const AccountManagement = ({ apiEndpoint, clientId, collectionId, onError, class
13832
13967
  setError(undefined);
13833
13968
  setSuccess(undefined);
13834
13969
  try {
13835
- console.log('[AccountManagement] Updating custom fields:', customFieldValues);
13970
+ await auth.updateContactCustomFields?.(customFieldValues);
13836
13971
  await auth.updateContactCustomFields?.(customFieldValues);
13837
13972
  setSuccess('Profile updated successfully!');
13838
13973
  setEditingSection(null);
@@ -13958,7 +14093,8 @@ const AccountManagement = ({ apiEndpoint, clientId, collectionId, onError, class
13958
14093
  setError('Please type DELETE to confirm account deletion');
13959
14094
  return;
13960
14095
  }
13961
- if (!deletePassword) {
14096
+ // Only require password for users who have one
14097
+ if (hasPassword !== false && !deletePassword) {
13962
14098
  setError('Password is required');
13963
14099
  return;
13964
14100
  }
@@ -13988,7 +14124,7 @@ const AccountManagement = ({ apiEndpoint, clientId, collectionId, onError, class
13988
14124
  }
13989
14125
  return (jsxs("div", { className: `account-management ${className}`, style: { maxWidth: '600px' }, children: [error && (jsx("div", { className: "auth-error", role: "alert", children: error })), success && (jsx("div", { className: "auth-success", role: "alert", children: success })), showProfileSection && (jsxs("section", { className: "account-section", children: [jsxs("div", { className: "account-field", children: [jsxs("div", { className: "field-info", children: [jsx("label", { className: "field-label", children: "Display Name" }), jsx("div", { className: "field-value", children: profile?.displayName || 'Not set' })] }), editingSection !== 'profile' && (jsx("button", { type: "button", onClick: () => setEditingSection('profile'), className: "auth-button button-secondary", children: "Change" }))] }), editingSection === 'profile' && (jsxs("form", { onSubmit: handleUpdateProfile, className: "edit-form", children: [jsx("div", { className: "form-group", children: jsx("input", { id: "displayName", type: "text", value: displayName, onChange: (e) => setDisplayName(e.target.value), placeholder: "Your display name", className: "auth-input" }) }), jsxs("div", { className: "button-group", children: [jsx("button", { type: "submit", className: "auth-button", disabled: loading, children: loading ? 'Saving...' : 'Save' }), jsx("button", { type: "button", onClick: cancelEdit, className: "auth-button button-secondary", children: "Cancel" })] })] }))] })), showCustomFields && collectionId && editableCustomFields.length > 0 && (jsxs("section", { className: "account-section", children: [jsx("h3", { className: "section-title", style: { fontSize: '0.9rem', fontWeight: 600, marginBottom: '12px' }, children: "Additional Information" }), editingSection !== 'customFields' ? (jsxs(Fragment, { children: [editableCustomFields.map((field) => (jsx("div", { className: "account-field", children: jsxs("div", { className: "field-info", children: [jsx("label", { className: "field-label", children: field.label }), jsx("div", { className: "field-value", children: customFieldValues[field.key] !== undefined && customFieldValues[field.key] !== ''
13990
14126
  ? String(customFieldValues[field.key])
13991
- : 'Not set' })] }) }, field.key))), jsx("button", { type: "button", onClick: () => setEditingSection('customFields'), className: "auth-button button-secondary", style: { marginTop: '8px' }, children: "Edit Information" })] })) : (jsxs("form", { onSubmit: handleUpdateCustomFields, className: "edit-form", children: [editableCustomFields.map((field) => (jsx(SchemaFieldRenderer, { field: field, value: customFieldValues[field.key], onChange: handleCustomFieldChange, disabled: loading }, field.key))), jsxs("div", { className: "button-group", children: [jsx("button", { type: "submit", className: "auth-button", disabled: loading, children: loading ? 'Saving...' : 'Save' }), jsx("button", { type: "button", onClick: cancelEdit, className: "auth-button button-secondary", children: "Cancel" })] })] }))] })), showEmailSection && (jsxs("section", { className: "account-section", children: [jsxs("div", { className: "account-field", children: [jsxs("div", { className: "field-info", children: [jsx("label", { className: "field-label", children: "Email Address" }), jsxs("div", { className: "field-value", children: [profile?.email || 'Not set', profile?.emailVerified && (jsx("span", { className: "verification-badge verified", children: "Verified" })), profile?.email && !profile?.emailVerified && (jsx("span", { className: "verification-badge unverified", children: "Unverified" }))] })] }), editingSection !== 'email' && (jsx("button", { type: "button", onClick: () => setEditingSection('email'), className: "auth-button button-secondary", children: "Change Email" }))] }), editingSection === 'email' && (jsxs("form", { onSubmit: handleChangeEmail, className: "edit-form", children: [jsxs("div", { className: "form-group", children: [jsx("label", { htmlFor: "newEmail", children: "New Email" }), jsx("input", { id: "newEmail", type: "email", value: newEmail, onChange: (e) => setNewEmail(e.target.value), placeholder: "new.email@example.com", className: "auth-input", required: true })] }), jsxs("div", { className: "form-group", children: [jsx("label", { htmlFor: "emailPassword", children: "Confirm Password" }), jsx("input", { id: "emailPassword", type: "password", value: emailPassword, onChange: (e) => setEmailPassword(e.target.value), placeholder: "Enter your password", className: "auth-input", required: true })] }), jsxs("div", { className: "button-group", children: [jsx("button", { type: "submit", className: "auth-button", disabled: loading, children: loading ? 'Changing...' : 'Change Email' }), jsx("button", { type: "button", onClick: cancelEdit, className: "auth-button button-secondary", children: "Cancel" })] })] }))] })), showPasswordSection && (jsxs("section", { className: "account-section", children: [jsxs("div", { className: "account-field", children: [jsxs("div", { className: "field-info", children: [jsx("label", { className: "field-label", children: "Password" }), jsx("div", { className: "field-value", children: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" })] }), editingSection !== 'password' && (jsx("button", { type: "button", onClick: () => setEditingSection('password'), className: "auth-button button-secondary", children: "Change Password" }))] }), editingSection === 'password' && (jsxs("form", { onSubmit: handleChangePassword, className: "edit-form", children: [jsxs("div", { className: "form-group", children: [jsx("label", { htmlFor: "currentPassword", children: "Current Password" }), jsx("input", { id: "currentPassword", type: "password", value: currentPassword, onChange: (e) => setCurrentPassword(e.target.value), placeholder: "Enter current password", className: "auth-input", required: true })] }), jsxs("div", { className: "form-group", children: [jsx("label", { htmlFor: "newPassword", children: "New Password" }), jsx("input", { id: "newPassword", type: "password", value: newPassword, onChange: (e) => setNewPassword(e.target.value), placeholder: "Enter new password", className: "auth-input", required: true, minLength: 6 })] }), jsxs("div", { className: "form-group", children: [jsx("label", { htmlFor: "confirmPassword", children: "Confirm New Password" }), jsx("input", { id: "confirmPassword", type: "password", value: confirmPassword, onChange: (e) => setConfirmPassword(e.target.value), placeholder: "Confirm new password", className: "auth-input", required: true, minLength: 6 })] }), jsxs("div", { className: "button-group", children: [jsx("button", { type: "submit", className: "auth-button", disabled: loading, children: loading ? 'Changing...' : 'Change Password' }), jsx("button", { type: "button", onClick: cancelEdit, className: "auth-button button-secondary", children: "Cancel" })] })] }))] })), showPhoneSection && (jsxs("section", { className: "account-section", children: [jsxs("div", { className: "account-field", children: [jsxs("div", { className: "field-info", children: [jsx("label", { className: "field-label", children: "Phone Number" }), jsx("div", { className: "field-value", children: profile?.phoneNumber || 'Not set' })] }), editingSection !== 'phone' && (jsx("button", { type: "button", onClick: () => setEditingSection('phone'), className: "auth-button button-secondary", children: "Change Phone" }))] }), editingSection === 'phone' && (jsxs("form", { onSubmit: handleUpdatePhone, className: "edit-form", children: [jsxs("div", { className: "form-group", children: [jsx("label", { htmlFor: "newPhone", children: "New Phone Number" }), jsx("input", { id: "newPhone", type: "tel", value: newPhone, onChange: (e) => setNewPhone(e.target.value), placeholder: "+1234567890", className: "auth-input", required: true })] }), !phoneCodeSent ? (jsxs("div", { className: "button-group", children: [jsx("button", { type: "button", onClick: handleSendPhoneCode, className: "auth-button", disabled: loading || !newPhone, children: loading ? 'Sending...' : 'Send Code' }), jsx("button", { type: "button", onClick: cancelEdit, className: "auth-button button-secondary", children: "Cancel" })] })) : (jsxs(Fragment, { children: [jsxs("div", { className: "form-group", children: [jsx("label", { htmlFor: "phoneCode", children: "Verification Code" }), jsx("input", { id: "phoneCode", type: "text", value: phoneCode, onChange: (e) => setPhoneCode(e.target.value), placeholder: "Enter 6-digit code", className: "auth-input", required: true, maxLength: 6 })] }), jsxs("div", { className: "button-group", children: [jsx("button", { type: "submit", className: "auth-button", disabled: loading, children: loading ? 'Verifying...' : 'Verify & Save' }), jsx("button", { type: "button", onClick: cancelEdit, className: "auth-button button-secondary", children: "Cancel" })] })] }))] }))] })), showDeleteAccount && (jsxs("section", { className: "account-section danger-zone", children: [jsx("h3", { className: "section-title text-danger", children: "Danger Zone" }), !showDeleteConfirm ? (jsx("button", { type: "button", onClick: () => setShowDeleteConfirm(true), className: "auth-button button-danger", children: "Delete Account" })) : (jsxs("div", { className: "delete-confirm", children: [jsx("p", { className: "warning-text", children: "\u26A0\uFE0F This action cannot be undone. This will permanently delete your account and all associated data." }), jsxs("div", { className: "form-group", children: [jsx("label", { htmlFor: "deletePassword", children: "Confirm Password" }), jsx("input", { id: "deletePassword", type: "password", value: deletePassword, onChange: (e) => setDeletePassword(e.target.value), placeholder: "Enter your password", className: "auth-input" })] }), jsxs("div", { className: "form-group", children: [jsx("label", { htmlFor: "deleteConfirm", children: "Type DELETE to confirm" }), jsx("input", { id: "deleteConfirm", type: "text", value: deleteConfirmText, onChange: (e) => setDeleteConfirmText(e.target.value), placeholder: "DELETE", className: "auth-input" })] }), jsxs("div", { className: "button-group", children: [jsx("button", { type: "button", onClick: handleDeleteAccount, className: "auth-button button-danger", disabled: loading, children: loading ? 'Deleting...' : 'Permanently Delete Account' }), jsx("button", { type: "button", onClick: () => {
14127
+ : 'Not set' })] }) }, field.key))), jsx("button", { type: "button", onClick: () => setEditingSection('customFields'), className: "auth-button button-secondary", style: { marginTop: '8px' }, children: "Edit Information" })] })) : (jsxs("form", { onSubmit: handleUpdateCustomFields, className: "edit-form", children: [editableCustomFields.map((field) => (jsx(SchemaFieldRenderer, { field: field, value: customFieldValues[field.key], onChange: handleCustomFieldChange, disabled: loading }, field.key))), jsxs("div", { className: "button-group", children: [jsx("button", { type: "submit", className: "auth-button", disabled: loading, children: loading ? 'Saving...' : 'Save' }), jsx("button", { type: "button", onClick: cancelEdit, className: "auth-button button-secondary", children: "Cancel" })] })] }))] })), showEmailSection && (jsxs("section", { className: "account-section", children: [jsxs("div", { className: "account-field", children: [jsxs("div", { className: "field-info", children: [jsx("label", { className: "field-label", children: "Email Address" }), jsxs("div", { className: "field-value", children: [profile?.email || 'Not set', profile?.emailVerified && (jsx("span", { className: "verification-badge verified", children: "Verified" })), profile?.email && !profile?.emailVerified && (jsx("span", { className: "verification-badge unverified", children: "Unverified" }))] }), isGoogleOnly && (jsx("div", { className: "field-hint", style: { fontSize: '0.8rem', color: 'var(--color-muted-foreground, #888)', marginTop: '4px' }, children: "This email is managed by your Google account" }))] }), editingSection !== 'email' && !isGoogleOnly && !isPhoneOnly && (jsx("button", { type: "button", onClick: () => setEditingSection('email'), className: "auth-button button-secondary", children: "Change Email" }))] }), editingSection === 'email' && !isGoogleOnly && !isPhoneOnly && (jsxs("form", { onSubmit: handleChangeEmail, className: "edit-form", children: [jsxs("div", { className: "form-group", children: [jsx("label", { htmlFor: "newEmail", children: "New Email" }), jsx("input", { id: "newEmail", type: "email", value: newEmail, onChange: (e) => setNewEmail(e.target.value), placeholder: "new.email@example.com", className: "auth-input", required: true })] }), jsxs("div", { className: "form-group", children: [jsx("label", { htmlFor: "emailPassword", children: "Confirm Password" }), jsx("input", { id: "emailPassword", type: "password", value: emailPassword, onChange: (e) => setEmailPassword(e.target.value), placeholder: "Enter your password", className: "auth-input", required: true })] }), jsxs("div", { className: "button-group", children: [jsx("button", { type: "submit", className: "auth-button", disabled: loading, children: loading ? 'Changing...' : 'Change Email' }), jsx("button", { type: "button", onClick: cancelEdit, className: "auth-button button-secondary", children: "Cancel" })] })] }))] })), showPasswordSection && !isGoogleOnly && !isPhoneOnly && (jsxs("section", { className: "account-section", children: [jsxs("div", { className: "account-field", children: [jsxs("div", { className: "field-info", children: [jsx("label", { className: "field-label", children: "Password" }), jsx("div", { className: "field-value", children: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" })] }), editingSection !== 'password' && (jsx("button", { type: "button", onClick: () => setEditingSection('password'), className: "auth-button button-secondary", children: "Change Password" }))] }), editingSection === 'password' && (jsxs("form", { onSubmit: handleChangePassword, className: "edit-form", children: [jsxs("div", { className: "form-group", children: [jsx("label", { htmlFor: "currentPassword", children: "Current Password" }), jsx("input", { id: "currentPassword", type: "password", value: currentPassword, onChange: (e) => setCurrentPassword(e.target.value), placeholder: "Enter current password", className: "auth-input", required: true })] }), jsxs("div", { className: "form-group", children: [jsx("label", { htmlFor: "newPassword", children: "New Password" }), jsx("input", { id: "newPassword", type: "password", value: newPassword, onChange: (e) => setNewPassword(e.target.value), placeholder: "Enter new password", className: "auth-input", required: true, minLength: 6 })] }), jsxs("div", { className: "form-group", children: [jsx("label", { htmlFor: "confirmPassword", children: "Confirm New Password" }), jsx("input", { id: "confirmPassword", type: "password", value: confirmPassword, onChange: (e) => setConfirmPassword(e.target.value), placeholder: "Confirm new password", className: "auth-input", required: true, minLength: 6 })] }), jsxs("div", { className: "button-group", children: [jsx("button", { type: "submit", className: "auth-button", disabled: loading, children: loading ? 'Changing...' : 'Change Password' }), jsx("button", { type: "button", onClick: cancelEdit, className: "auth-button button-secondary", children: "Cancel" })] })] }))] })), showPhoneSection && (jsxs("section", { className: "account-section", children: [jsxs("div", { className: "account-field", children: [jsxs("div", { className: "field-info", children: [jsx("label", { className: "field-label", children: "Phone Number" }), jsx("div", { className: "field-value", children: profile?.phoneNumber || 'Not set' })] }), editingSection !== 'phone' && (jsx("button", { type: "button", onClick: () => setEditingSection('phone'), className: "auth-button button-secondary", children: "Change Phone" }))] }), editingSection === 'phone' && (jsxs("form", { onSubmit: handleUpdatePhone, className: "edit-form", children: [jsxs("div", { className: "form-group", children: [jsx("label", { htmlFor: "newPhone", children: "New Phone Number" }), jsx("input", { id: "newPhone", type: "tel", value: newPhone, onChange: (e) => setNewPhone(e.target.value), placeholder: "+1234567890", className: "auth-input", required: true })] }), !phoneCodeSent ? (jsxs("div", { className: "button-group", children: [jsx("button", { type: "button", onClick: handleSendPhoneCode, className: "auth-button", disabled: loading || !newPhone, children: loading ? 'Sending...' : 'Send Code' }), jsx("button", { type: "button", onClick: cancelEdit, className: "auth-button button-secondary", children: "Cancel" })] })) : (jsxs(Fragment, { children: [jsxs("div", { className: "form-group", children: [jsx("label", { htmlFor: "phoneCode", children: "Verification Code" }), jsx("input", { id: "phoneCode", type: "text", value: phoneCode, onChange: (e) => setPhoneCode(e.target.value), placeholder: "Enter 6-digit code", className: "auth-input", required: true, maxLength: 6 })] }), jsxs("div", { className: "button-group", children: [jsx("button", { type: "submit", className: "auth-button", disabled: loading, children: loading ? 'Verifying...' : 'Verify & Save' }), jsx("button", { type: "button", onClick: cancelEdit, className: "auth-button button-secondary", children: "Cancel" })] })] }))] }))] })), showDeleteAccount && (jsxs("section", { className: "account-section danger-zone", children: [jsx("h3", { className: "section-title text-danger", children: "Danger Zone" }), !showDeleteConfirm ? (jsx("button", { type: "button", onClick: () => setShowDeleteConfirm(true), className: "auth-button button-danger", children: "Delete Account" })) : (jsxs("div", { className: "delete-confirm", children: [jsx("p", { className: "warning-text", children: "\u26A0\uFE0F This action cannot be undone. This will permanently delete your account and all associated data." }), hasPassword !== false && (jsxs("div", { className: "form-group", children: [jsx("label", { htmlFor: "deletePassword", children: "Confirm Password" }), jsx("input", { id: "deletePassword", type: "password", value: deletePassword, onChange: (e) => setDeletePassword(e.target.value), placeholder: "Enter your password", className: "auth-input" })] })), jsxs("div", { className: "form-group", children: [jsx("label", { htmlFor: "deleteConfirm", children: "Type DELETE to confirm" }), jsx("input", { id: "deleteConfirm", type: "text", value: deleteConfirmText, onChange: (e) => setDeleteConfirmText(e.target.value), placeholder: "DELETE", className: "auth-input" })] }), jsxs("div", { className: "button-group", children: [jsx("button", { type: "button", onClick: handleDeleteAccount, className: "auth-button button-danger", disabled: loading, children: loading ? 'Deleting...' : 'Permanently Delete Account' }), jsx("button", { type: "button", onClick: () => {
13992
14128
  setShowDeleteConfirm(false);
13993
14129
  setDeletePassword('');
13994
14130
  setDeleteConfirmText('');