@skylabs-digital/react-identity-access 2.24.0 → 2.26.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.
- package/README.md +560 -150
- package/dist/components/LoginForm.d.ts +5 -0
- package/dist/components/LoginForm.d.ts.map +1 -1
- package/dist/components/MagicLinkForm.d.ts +10 -0
- package/dist/components/MagicLinkForm.d.ts.map +1 -1
- package/dist/components/MagicLinkVerify.d.ts +3 -0
- package/dist/components/MagicLinkVerify.d.ts.map +1 -1
- package/dist/components/PasswordRecoveryForm.d.ts +5 -0
- package/dist/components/PasswordRecoveryForm.d.ts.map +1 -1
- package/dist/components/SignupForm.d.ts +4 -0
- package/dist/components/SignupForm.d.ts.map +1 -1
- package/dist/components/TenantSelector.d.ts +13 -1
- package/dist/components/TenantSelector.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.es.js +1212 -1161
- package/dist/index.es.js.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/providers/AppProvider.d.ts.map +1 -1
- package/dist/providers/AuthProvider.d.ts.map +1 -1
- package/dist/providers/TenantProvider.d.ts.map +1 -1
- package/dist/services/SessionManager.d.ts.map +1 -1
- package/dist/utils/crossDomainAuth.d.ts.map +1 -1
- package/package.json +9 -9
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/services/HttpService.ts","../src/services/AppApiService.ts","../src/providers/AppProvider.tsx","../src/errors/SessionErrors.ts","../src/services/SessionManager.ts","../src/services/AuthApiService.ts","../src/services/RoleApiService.ts","../src/services/UserApiService.ts","../src/services/TenantApiService.ts","../src/utils/tenantDetection.ts","../src/utils/crossDomainAuth.ts","../src/providers/TenantProvider.tsx","../src/providers/AuthProvider.tsx","../src/services/FeatureFlagApiService.ts","../src/providers/FeatureFlagProvider.tsx","../src/services/SubscriptionApiService.ts","../src/providers/SubscriptionProvider.tsx","../src/types/api.ts","../src/types/zoneRouting.ts","../src/providers/RoutingProvider.tsx","../src/components/Protected.tsx","../src/components/ProtectedRoute.tsx","../src/components/TenantRoute.tsx","../src/components/LandingRoute.tsx","../src/components/ZoneRoute.tsx","../src/components/SubscriptionGuard.tsx","../src/components/FeatureFlag.tsx","../src/components/LoginForm.tsx","../src/components/SignupForm.tsx","../src/components/MagicLinkForm.tsx","../src/components/MagicLinkVerify.tsx","../src/components/PasswordRecoveryForm.tsx","../src/components/AppLoader.tsx","../src/components/TenantSelector.tsx","../src/services/PermissionApiService.ts","../src/services/SubscriptionPlanApiService.ts","../src/services/HealthApiService.ts","../src/utils/mappers.ts","../src/hooks/useZoneNavigation.ts"],"sourcesContent":["import type { SessionManager } from './SessionManager';\n\nexport interface RequestOptions {\n headers?: Record<string, string>;\n timeout?: number;\n skipAuth?: boolean; // Skip automatic auth header injection\n}\n\nexport class HttpService {\n private baseUrl: string;\n private timeout: number;\n private sessionManager?: SessionManager;\n\n constructor(baseUrl: string, timeout = 10000) {\n this.baseUrl = baseUrl.replace(/\\/$/, ''); // Remove trailing slash\n this.timeout = timeout;\n }\n\n setSessionManager(sessionManager: SessionManager): void {\n this.sessionManager = sessionManager;\n }\n\n getBaseUrl(): string {\n return this.baseUrl;\n }\n\n private async executeRequest<T>(\n method: string,\n endpoint: string,\n data?: any,\n options?: RequestOptions\n ): Promise<T> {\n const url = `${this.baseUrl}${endpoint.startsWith('/') ? endpoint : `/${endpoint}`}`;\n const requestTimeout = options?.timeout || this.timeout;\n\n // Inject auth headers via SessionManager.getValidAccessToken()\n // SessionManager handles refresh, queue, retry, and error classification\n let requestHeaders: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...options?.headers,\n };\n\n if (!options?.skipAuth && this.sessionManager) {\n // SessionManager handles refresh, queue, retry, and error classification.\n // Throws SessionExpiredError | TokenRefreshTimeoutError | TokenRefreshError\n // which propagate to caller — they decide what to do.\n const accessToken = await this.sessionManager.getValidAccessToken();\n requestHeaders = { ...requestHeaders, Authorization: `Bearer ${accessToken}` };\n }\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), requestTimeout);\n\n try {\n const response = await fetch(url, {\n method,\n headers: requestHeaders,\n body: data ? JSON.stringify(data) : undefined,\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n\n // Handle empty responses\n const contentType = response.headers.get('content-type');\n if (!contentType || !contentType.includes('application/json')) {\n return {} as T;\n }\n\n return await response.json();\n } catch (error) {\n clearTimeout(timeoutId);\n\n if (error instanceof Error && error.name === 'AbortError') {\n throw new Error(`Request timeout after ${requestTimeout}ms`);\n }\n\n throw error;\n }\n }\n\n async get<T>(endpoint: string, options?: RequestOptions): Promise<T> {\n return this.executeRequest<T>('GET', endpoint, undefined, options);\n }\n\n async post<T>(endpoint: string, data: any, options?: RequestOptions): Promise<T> {\n return this.executeRequest<T>('POST', endpoint, data, options);\n }\n\n async put<T>(endpoint: string, data: any, options?: RequestOptions): Promise<T> {\n return this.executeRequest<T>('PUT', endpoint, data, options);\n }\n\n async delete<T>(endpoint: string, options?: RequestOptions): Promise<T> {\n return this.executeRequest<T>('DELETE', endpoint, undefined, options);\n }\n}\n","import { HttpService } from './HttpService';\nimport { SessionManager } from './SessionManager';\nimport type {\n ApiResponse,\n App,\n CreateAppRequest,\n PublicAppInfo,\n PaginationParams,\n} from '../types/api';\n\nexport class AppApiService {\n constructor(\n private httpService: HttpService,\n private sessionManager: SessionManager\n ) {}\n\n async createApp(request: CreateAppRequest): Promise<App> {\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const response = await this.httpService.post<ApiResponse<App>>('/apps/', request, {\n headers: authHeaders,\n });\n return response.data;\n }\n\n async getApps(params?: PaginationParams): Promise<{ apps: App[]; meta: any }> {\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const queryParams = new URLSearchParams();\n\n if (params?.page) queryParams.append('page', params.page.toString());\n if (params?.limit) queryParams.append('limit', params.limit.toString());\n if (params?.sortBy) queryParams.append('sortBy', params.sortBy);\n if (params?.sortOrder) queryParams.append('sortOrder', params.sortOrder);\n\n const url = `/apps/${queryParams.toString() ? `?${queryParams.toString()}` : ''}`;\n const response = await this.httpService.get<ApiResponse<App[]>>(url, {\n headers: authHeaders,\n });\n\n return {\n apps: response.data,\n meta: response.meta,\n };\n }\n\n async getAppById(id: string): Promise<App> {\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const response = await this.httpService.get<ApiResponse<App>>(`/apps/${id}`, {\n headers: authHeaders,\n });\n return response.data;\n }\n\n async updateApp(id: string, request: Partial<CreateAppRequest>): Promise<App> {\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const response = await this.httpService.put<ApiResponse<App>>(`/apps/${id}`, request, {\n headers: authHeaders,\n });\n return response.data;\n }\n\n async getPublicAppInfo(id: string): Promise<PublicAppInfo> {\n const response = await this.httpService.get<ApiResponse<PublicAppInfo>>(`/apps/${id}/public`);\n return response.data;\n }\n\n async setDefaultSubscriptionPlan(appId: string, planId: string): Promise<App> {\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const response = await this.httpService.put<ApiResponse<App>>(\n `/apps/${appId}/default-subscription-plan`,\n { planId },\n { headers: authHeaders }\n );\n return response.data;\n }\n\n async updateSettingsSchema(appId: string, schema: any, defaultSettings: any): Promise<App> {\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const response = await this.httpService.put<ApiResponse<App>>(\n `/apps/${appId}/settings-schema`,\n { schema, defaultSettings },\n { headers: authHeaders }\n );\n return response.data;\n }\n\n async exportConfig(appId: string): Promise<any> {\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const response = await this.httpService.get<ApiResponse<any>>(`/apps/${appId}/export-config`, {\n headers: authHeaders,\n });\n return response.data;\n }\n}\n","import {\n createContext,\n useContext,\n useMemo,\n ReactNode,\n useState,\n useEffect,\n useCallback,\n} from 'react';\nimport { HttpService } from '../services/HttpService';\nimport { AppApiService } from '../services/AppApiService';\nimport type { PublicAppInfo } from '../types/api';\n\n// RFC-003: Cache interface for app info\ninterface CachedAppInfo {\n data: PublicAppInfo;\n timestamp: number;\n appId: string;\n}\n\nexport interface AppConfig {\n baseUrl: string;\n appId: string;\n // RFC-003: Cache configuration\n cache?: {\n enabled?: boolean; // Default: true\n ttl?: number; // Time to live in milliseconds, default: 5 minutes\n storageKey?: string; // Default: 'app_cache_{appId}'\n };\n}\n\ninterface AppContextValue {\n appId: string;\n baseUrl: string;\n // App info with settings schema\n appInfo: PublicAppInfo | null;\n isAppLoading: boolean;\n appError: Error | null;\n retryApp: () => void;\n}\n\nconst AppContext = createContext<AppContextValue | null>(null);\n\ninterface AppProviderProps {\n config: AppConfig;\n children: ReactNode;\n}\n\nexport function AppProvider({ config, children }: AppProviderProps) {\n // RFC-003: Cache configuration with defaults\n const cacheConfig = useMemo(\n () => ({\n enabled: config.cache?.enabled ?? true,\n ttl: config.cache?.ttl ?? 5 * 60 * 1000, // 5 minutes default\n storageKey: config.cache?.storageKey ?? `app_cache_${config.appId}`,\n }),\n [config.cache, config.appId]\n );\n\n // RFC-003: Try to load from cache on initialization\n const [appInfo, setAppInfo] = useState<PublicAppInfo | null>(() => {\n if (!cacheConfig.enabled) return null;\n\n try {\n const cached = localStorage.getItem(cacheConfig.storageKey);\n if (!cached) return null;\n\n const parsed: CachedAppInfo = JSON.parse(cached);\n const now = Date.now();\n const age = now - parsed.timestamp;\n\n // Check if cache is still valid\n if (age < cacheConfig.ttl && parsed.appId === config.appId) {\n return parsed.data;\n }\n\n // Cache expired\n localStorage.removeItem(cacheConfig.storageKey);\n return null;\n } catch {\n return null;\n }\n });\n\n const [isAppLoading, setIsAppLoading] = useState(!appInfo); // Don't load if we have cache\n const [appError, setAppError] = useState<Error | null>(null);\n\n const contextValue = useMemo(() => {\n // Retry function for app loading\n const retryApp = () => {\n loadApp();\n };\n\n return {\n appId: config.appId,\n baseUrl: config.baseUrl,\n // App info\n appInfo,\n isAppLoading,\n appError,\n retryApp,\n };\n }, [config, appInfo, isAppLoading, appError]);\n\n // RFC-003: Load app info with caching\n const loadApp = useCallback(\n async (bypassCache = false) => {\n // Check cache first (unless bypassing)\n if (!bypassCache && cacheConfig.enabled && appInfo) {\n return; // Already have valid cached data\n }\n\n try {\n setIsAppLoading(true);\n setAppError(null);\n\n const httpService = new HttpService(config.baseUrl);\n const appApi = new AppApiService(httpService, {} as any); // SessionManager not needed for public endpoint\n const appData = await appApi.getPublicAppInfo(config.appId);\n setAppInfo(appData);\n\n // RFC-003: Save to cache\n if (cacheConfig.enabled) {\n try {\n const cacheData: CachedAppInfo = {\n data: appData,\n timestamp: Date.now(),\n appId: config.appId,\n };\n localStorage.setItem(cacheConfig.storageKey, JSON.stringify(cacheData));\n } catch (error) {\n console.warn('Failed to cache app info:', error);\n }\n }\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to load app information');\n setAppError(error);\n setAppInfo(null);\n } finally {\n setIsAppLoading(false);\n }\n },\n [config.baseUrl, config.appId, cacheConfig, appInfo]\n );\n\n // RFC-003: Background refresh for stale-while-revalidate\n const backgroundRefresh = useCallback(async () => {\n if (!cacheConfig.enabled || !appInfo) return;\n\n try {\n const cached = localStorage.getItem(cacheConfig.storageKey);\n if (!cached) return;\n\n const parsed: CachedAppInfo = JSON.parse(cached);\n const age = Date.now() - parsed.timestamp;\n\n // If cache is more than 50% expired, refresh in background\n if (age > cacheConfig.ttl * 0.5) {\n const httpService = new HttpService(config.baseUrl);\n const appApi = new AppApiService(httpService, {} as any);\n const appData = await appApi.getPublicAppInfo(config.appId);\n\n setAppInfo(appData);\n\n const cacheData: CachedAppInfo = {\n data: appData,\n timestamp: Date.now(),\n appId: config.appId,\n };\n localStorage.setItem(cacheConfig.storageKey, JSON.stringify(cacheData));\n }\n } catch (error) {\n console.warn('Background app refresh failed:', error);\n // Don't update error state - keep showing cached data\n }\n }, [config, cacheConfig, appInfo]);\n\n // RFC-003: Load app info on mount or do background refresh\n useEffect(() => {\n if (!appInfo) {\n loadApp();\n } else {\n // We have cached data, do background refresh\n backgroundRefresh();\n }\n }, []); // Only run on mount\n\n // No longer blocks children - loading state is exposed via context\n // Use AppLoader component to block until ready\n return <AppContext.Provider value={contextValue}>{children}</AppContext.Provider>;\n}\n\nexport function useApp(): AppContextValue {\n const context = useContext(AppContext);\n if (!context) {\n throw new Error('useApp must be used within an AppProvider');\n }\n return context;\n}\n\n/**\n * Optional hook that returns AppContext if available, null otherwise.\n */\nexport function useAppOptional(): AppContextValue | null {\n return useContext(AppContext);\n}\n\n// Backward compatibility\nexport const useApi = useApp;\n","/**\n * Session error classes for deterministic token refresh handling.\n *\n * These errors allow consumers to distinguish between:\n * - Fatal session errors (refresh token invalid → must logout)\n * - Timeout errors (queue wait exceeded → can retry)\n * - Transient refresh errors (network issues → system keeps retrying)\n */\n\nexport type SessionExpiredReason = 'token_expired' | 'token_invalid' | 'user_inactive';\n\n/**\n * Thrown when the refresh token is definitively invalid and the session must end.\n * Consumers should redirect to login when catching this error.\n */\nexport class SessionExpiredError extends Error {\n public readonly reason: SessionExpiredReason;\n\n constructor(reason: SessionExpiredReason, message?: string) {\n const defaultMessages: Record<SessionExpiredReason, string> = {\n token_expired: 'Refresh token has expired',\n token_invalid: 'Refresh token is invalid',\n user_inactive: 'User account is inactive',\n };\n super(message || defaultMessages[reason]);\n this.name = 'SessionExpiredError';\n this.reason = reason;\n }\n}\n\n/**\n * Thrown when a queued request exceeds its timeout waiting for a token refresh.\n * The refresh may still be in progress — the caller can retry.\n */\nexport class TokenRefreshTimeoutError extends Error {\n public readonly timeoutMs: number;\n\n constructor(timeoutMs: number) {\n super(`Token refresh timed out after ${timeoutMs}ms`);\n this.name = 'TokenRefreshTimeoutError';\n this.timeoutMs = timeoutMs;\n }\n}\n\n/**\n * Thrown when token refresh fails after all retries due to transient errors.\n * The proactive timer will keep retrying in the background.\n */\nexport class TokenRefreshError extends Error {\n public readonly attempts: number;\n public readonly lastError?: Error;\n\n constructor(attempts: number, lastError?: Error) {\n super(\n `Token refresh failed after ${attempts} attempts: ${lastError?.message || 'Unknown error'}`\n );\n this.name = 'TokenRefreshError';\n this.attempts = attempts;\n this.lastError = lastError;\n }\n}\n","import {\n SessionExpiredError,\n TokenRefreshTimeoutError,\n TokenRefreshError,\n} from '../errors/SessionErrors';\n\nexport interface TokenData {\n accessToken: string;\n refreshToken?: string;\n expiresAt?: number;\n expiresIn?: number;\n tokenType?: string;\n}\n\nexport interface JwtPayload {\n userId: string;\n email: string | null;\n phoneNumber: string | null;\n userType: string;\n role: string | null;\n tenantId: string | null;\n appId: string | null;\n iat?: number;\n exp?: number;\n}\n\nexport interface TokenStorage {\n get(): any;\n set(data: any): void;\n clear(): void;\n}\n\nexport interface SessionConfig {\n storageKey?: string;\n tenantSlug?: string | null;\n autoRefresh?: boolean;\n refreshThreshold?: number;\n /** @deprecated Use onSessionExpired instead */\n onRefreshFailed?: () => void;\n onSessionExpired?: (error: SessionExpiredError) => void;\n tokenStorage?: TokenStorage;\n baseUrl?: string;\n // New: deterministic refresh config\n proactiveRefreshMargin?: number; // ms before expiry to trigger proactive refresh (default: 60000)\n refreshQueueTimeout?: number; // ms before queued requests timeout (default: 10000)\n maxRefreshRetries?: number; // max retries per refresh attempt (default: 3)\n retryBackoffBase?: number; // base ms for exponential backoff (default: 1000)\n}\n\ninterface QueueEntry {\n resolve: (token: string) => void;\n reject: (error: Error) => void;\n timeoutId: ReturnType<typeof setTimeout>;\n}\n\nexport class SessionManager {\n // --- Singleton registry (keyed by storageKey) ---\n private static instances = new Map<string, SessionManager>();\n\n /**\n * Get or create a SessionManager instance for the given config.\n * Returns the same instance when called with the same storageKey/tenantSlug.\n * Mutable config (callbacks, baseUrl) is updated on the existing instance.\n */\n static getInstance(config: SessionConfig = {}): SessionManager {\n const key = SessionManager.resolveStorageKey(config);\n const existing = SessionManager.instances.get(key);\n if (existing) {\n existing.updateConfig(config);\n return existing;\n }\n const instance = new SessionManager(config);\n SessionManager.instances.set(key, instance);\n return instance;\n }\n\n /** Reset all singleton instances. For testing only. */\n static resetAllInstances(): void {\n for (const instance of SessionManager.instances.values()) {\n instance.destroy();\n }\n SessionManager.instances.clear();\n }\n\n private static resolveStorageKey(config: SessionConfig): string {\n if (config.storageKey) return config.storageKey;\n if (config.tenantSlug !== undefined) {\n return config.tenantSlug ? `auth_tokens_${config.tenantSlug}` : 'auth_tokens';\n }\n return 'auth_tokens';\n }\n\n private storageKey: string;\n private autoRefresh: boolean;\n private refreshThreshold: number;\n private baseUrl: string;\n private onRefreshFailed?: () => void;\n private onSessionExpired?: (error: SessionExpiredError) => void;\n private tokenStorage: TokenStorage;\n\n // New config\n private proactiveRefreshMargin: number;\n private refreshQueueTimeout: number;\n private maxRefreshRetries: number;\n private retryBackoffBase: number;\n\n // Refresh state\n private refreshPromise: Promise<void> | null = null;\n private refreshQueue: QueueEntry[] = [];\n private proactiveTimerId: ReturnType<typeof setTimeout> | null = null;\n private backgroundRetryTimerId: ReturnType<typeof setTimeout> | null = null;\n private isDestroyed = false;\n private sessionGeneration = 0;\n\n constructor(config: SessionConfig = {}) {\n if (config.tenantSlug !== undefined) {\n this.storageKey = config.tenantSlug ? `auth_tokens_${config.tenantSlug}` : 'auth_tokens';\n } else {\n this.storageKey = config.storageKey || 'auth_tokens';\n }\n\n this.autoRefresh = config.autoRefresh ?? true;\n this.refreshThreshold = config.refreshThreshold || 300000; // 5 minutes\n this.onRefreshFailed = config.onRefreshFailed;\n this.onSessionExpired = config.onSessionExpired;\n this.baseUrl = config.baseUrl || '';\n\n // New config with defaults\n this.proactiveRefreshMargin = config.proactiveRefreshMargin ?? 60000; // 1 minute\n this.refreshQueueTimeout = config.refreshQueueTimeout ?? 10000; // 10 seconds\n this.maxRefreshRetries = config.maxRefreshRetries ?? 3;\n this.retryBackoffBase = config.retryBackoffBase ?? 1000; // 1 second\n\n this.tokenStorage = config.tokenStorage || this.createTokenStorage(this.storageKey);\n\n // Schedule proactive refresh if we already have tokens\n this.scheduleProactiveRefresh();\n }\n\n /** Update mutable config (callbacks, baseUrl) on an existing instance. */\n private updateConfig(config: SessionConfig): void {\n if (config.onSessionExpired !== undefined) this.onSessionExpired = config.onSessionExpired;\n if (config.onRefreshFailed !== undefined) this.onRefreshFailed = config.onRefreshFailed;\n if (config.baseUrl) this.baseUrl = config.baseUrl;\n }\n\n // --- Storage helpers ---\n\n private createTokenStorage(storageKey: string): TokenStorage {\n return {\n get: () => {\n try {\n const stored = localStorage.getItem(storageKey);\n return stored ? JSON.parse(stored) : null;\n } catch {\n return null;\n }\n },\n set: (data: any) => {\n try {\n localStorage.setItem(storageKey, JSON.stringify(data));\n } catch {\n // Handle storage errors silently\n }\n },\n clear: () => {\n try {\n localStorage.removeItem(storageKey);\n } catch {\n // Handle storage errors silently\n }\n },\n };\n }\n\n // --- Token CRUD ---\n\n /**\n * Extract the `exp` claim from a JWT and convert to milliseconds.\n * Returns undefined if the token cannot be decoded or has no exp.\n */\n private static extractJwtExpiry(accessToken: string): number | undefined {\n try {\n const parts = accessToken.split('.');\n if (parts.length !== 3) return undefined;\n const payload = JSON.parse(atob(parts[1].replace(/-/g, '+').replace(/_/g, '/')));\n if (typeof payload.exp === 'number') {\n return payload.exp * 1000; // JWT exp is in seconds\n }\n return undefined;\n } catch {\n return undefined;\n }\n }\n\n setTokens(tokens: TokenData): void {\n const expiresAt =\n tokens.expiresAt ||\n (tokens.expiresIn ? Date.now() + tokens.expiresIn * 1000 : undefined) ||\n SessionManager.extractJwtExpiry(tokens.accessToken);\n\n const tokenData: TokenData = {\n ...tokens,\n expiresAt,\n };\n\n // Merge with existing storage to preserve non-token data (e.g. user)\n const currentData = this.tokenStorage.get() || {};\n this.tokenStorage.set({ ...currentData, ...tokenData });\n\n // Reschedule proactive refresh with new expiry\n this.scheduleProactiveRefresh();\n }\n\n getTokens(): TokenData | null {\n const { accessToken, refreshToken, expiresAt, expiresIn, tokenType } =\n this.tokenStorage.get() || {};\n\n if (!accessToken) {\n return null;\n }\n\n // Fallback: derive expiresAt from JWT exp claim when not stored\n const resolvedExpiresAt = expiresAt || SessionManager.extractJwtExpiry(accessToken);\n\n return {\n accessToken,\n refreshToken,\n expiresAt: resolvedExpiresAt,\n expiresIn,\n tokenType,\n };\n }\n\n clearTokens(): void {\n this.tokenStorage.clear();\n }\n\n isTokenExpired(token?: TokenData): boolean {\n const tokens = token || this.getTokens();\n if (!tokens?.expiresAt) return false;\n return Date.now() >= tokens.expiresAt;\n }\n\n shouldRefreshToken(token?: TokenData): boolean {\n const tokens = token || this.getTokens();\n if (!tokens?.expiresAt || !this.autoRefresh) return false;\n return Date.now() >= tokens.expiresAt - this.refreshThreshold;\n }\n\n getAccessToken(): string | null {\n const tokens = this.getTokens();\n return tokens?.accessToken || null;\n }\n\n // --- Proactive refresh timer ---\n\n private scheduleProactiveRefresh(): void {\n this.cancelProactiveTimer();\n if (!this.autoRefresh || this.isDestroyed) return;\n\n const tokens = this.getTokens();\n if (!tokens?.expiresAt || !tokens.refreshToken) return;\n\n const refreshAt = tokens.expiresAt - this.proactiveRefreshMargin;\n const delay = refreshAt - Date.now();\n\n if (delay <= 0) {\n // Already past the proactive refresh point — refresh now\n this.backgroundRefresh();\n return;\n }\n\n this.proactiveTimerId = setTimeout(() => {\n this.backgroundRefresh();\n }, delay);\n }\n\n private cancelProactiveTimer(): void {\n if (this.proactiveTimerId !== null) {\n clearTimeout(this.proactiveTimerId);\n this.proactiveTimerId = null;\n }\n if (this.backgroundRetryTimerId !== null) {\n clearTimeout(this.backgroundRetryTimerId);\n this.backgroundRetryTimerId = null;\n }\n }\n\n private backgroundRefresh(): void {\n if (this.isDestroyed) return;\n\n const tokens = this.getTokens();\n if (!tokens?.refreshToken) return;\n\n // If a refresh is already in progress (e.g. from getValidAccessToken), skip\n if (this.refreshPromise) return;\n\n const gen = this.sessionGeneration;\n\n // Use the shared refresh mechanism so refreshPromise is set.\n // This ensures concurrent getValidAccessToken() calls queue behind\n // this refresh instead of starting a duplicate one.\n this.startRefreshAndResolveQueue(tokens.refreshToken)\n .then(() => {\n // Success — scheduleProactiveRefresh is called by setTokens()\n })\n .catch(error => {\n if (error instanceof SessionExpiredError) {\n // Fatal — already handled by startRefreshAndResolveQueue\n } else if (this.sessionGeneration === gen) {\n // Transient — schedule background retry in 30s (only if session wasn't cleared)\n console.warn(\n '[SessionManager] Background refresh failed, retrying in 30s:',\n error.message\n );\n this.backgroundRetryTimerId = setTimeout(() => {\n this.backgroundRefresh();\n }, 30000);\n }\n });\n }\n\n /**\n * Wait for any in-progress token refresh to settle.\n * Returns true if refresh succeeded, false if it failed or no refresh was pending.\n */\n async waitForPendingRefresh(): Promise<boolean> {\n if (!this.refreshPromise) return false;\n try {\n await this.refreshPromise;\n return true;\n } catch {\n return false;\n }\n }\n\n // --- Core: getValidAccessToken with queue + timeout ---\n\n /**\n * Get a valid access token. If the token needs refresh, handles the refresh\n * with queuing, timeout, and retry logic.\n *\n * @throws {SessionExpiredError} if refresh token is invalid/expired → caller should logout\n * @throws {TokenRefreshTimeoutError} if queue wait exceeds timeout → caller can retry\n * @throws {TokenRefreshError} if refresh fails after all retries\n */\n async getValidAccessToken(): Promise<string> {\n const tokens = this.getTokens();\n\n // No tokens at all\n if (!tokens?.accessToken) {\n const error = new SessionExpiredError('token_invalid', 'No tokens available');\n this.handleSessionExpired(error);\n throw error;\n }\n\n // Token is valid and not near expiry — return immediately\n if (!this.shouldRefreshToken(tokens) && !this.isTokenExpired(tokens)) {\n return tokens.accessToken;\n }\n\n // Token needs refresh — no refresh token available\n if (!tokens.refreshToken) {\n const error = new SessionExpiredError('token_invalid', 'No refresh token available');\n this.handleSessionExpired(error);\n throw error;\n }\n\n // If refresh is already in progress, queue with timeout\n if (this.refreshPromise) {\n return this.enqueueForToken();\n }\n\n // Start the refresh process\n return this.startRefreshAndResolveQueue(tokens.refreshToken);\n }\n\n /**\n * Backward-compatible getAuthHeaders — now delegates to getValidAccessToken.\n */\n async getAuthHeaders(): Promise<Record<string, string>> {\n try {\n const accessToken = await this.getValidAccessToken();\n return { Authorization: `Bearer ${accessToken}` };\n } catch (error) {\n // Maintain backward compat: return empty headers instead of throwing\n // for code that expects the old behavior\n if (error instanceof SessionExpiredError) {\n // Legacy callback\n if (this.onRefreshFailed) {\n this.onRefreshFailed();\n }\n }\n return {};\n }\n }\n\n private enqueueForToken(): Promise<string> {\n return new Promise<string>((resolve, reject) => {\n const timeoutId = setTimeout(() => {\n // Remove this entry from the queue\n const idx = this.refreshQueue.findIndex(e => e.timeoutId === timeoutId);\n if (idx !== -1) {\n this.refreshQueue.splice(idx, 1);\n }\n reject(new TokenRefreshTimeoutError(this.refreshQueueTimeout));\n }, this.refreshQueueTimeout);\n\n this.refreshQueue.push({ resolve, reject, timeoutId });\n });\n }\n\n private async startRefreshAndResolveQueue(refreshToken: string): Promise<string> {\n // Create the shared promise\n this.refreshPromise = this.executeRefreshWithRetry(refreshToken);\n\n try {\n await this.refreshPromise;\n\n // Refresh successful — get new token\n const newTokens = this.getTokens();\n const newAccessToken = newTokens?.accessToken || '';\n\n // Resolve all queued requests\n this.resolveQueue(newAccessToken);\n\n return newAccessToken;\n } catch (error) {\n const err = error instanceof Error ? error : new Error('Token refresh failed');\n\n if (err instanceof SessionExpiredError) {\n // Fatal — reject all and trigger session expiry\n this.rejectQueue(err);\n this.handleSessionExpired(err);\n } else {\n // Transient — reject all queued with the error\n this.rejectQueue(err);\n }\n\n throw err;\n } finally {\n this.refreshPromise = null;\n }\n }\n\n private resolveQueue(accessToken: string): void {\n const queue = [...this.refreshQueue];\n this.refreshQueue = [];\n for (const entry of queue) {\n clearTimeout(entry.timeoutId);\n entry.resolve(accessToken);\n }\n }\n\n private rejectQueue(error: Error): void {\n const queue = [...this.refreshQueue];\n this.refreshQueue = [];\n for (const entry of queue) {\n clearTimeout(entry.timeoutId);\n entry.reject(error);\n }\n }\n\n // --- Refresh with retry + error classification ---\n\n private async executeRefreshWithRetry(refreshToken: string): Promise<void> {\n let lastError: Error | undefined;\n const gen = this.sessionGeneration;\n\n for (let attempt = 0; attempt <= this.maxRefreshRetries; attempt++) {\n // Bail out if session was cleared during retry (logout, session expired, etc.)\n if (this.sessionGeneration !== gen) {\n throw new SessionExpiredError('token_invalid', 'Session cleared during refresh');\n }\n\n try {\n await this.performTokenRefresh(refreshToken, gen);\n return; // Success\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n\n // Fatal errors — do not retry\n if (err instanceof SessionExpiredError) {\n throw err;\n }\n\n lastError = err;\n\n // Don't wait after the last attempt\n if (attempt < this.maxRefreshRetries) {\n const backoff = this.retryBackoffBase * Math.pow(2, attempt);\n await this.sleep(backoff);\n }\n }\n }\n\n // All retries exhausted\n throw new TokenRefreshError(this.maxRefreshRetries + 1, lastError);\n }\n\n /**\n * Single refresh attempt with error classification.\n * Throws SessionExpiredError for fatal errors (no retry).\n * Throws generic Error for transient errors (will be retried).\n */\n private async performTokenRefresh(refreshToken: string, gen: number): Promise<void> {\n if (!this.baseUrl) {\n throw new Error('Base URL not configured for token refresh');\n }\n\n const url = `${this.baseUrl}/auth/refresh`;\n\n let response: Response;\n try {\n response = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ refreshToken }),\n });\n } catch (networkError) {\n // Network error (e.g., TypeError: Failed to fetch) — transient\n throw networkError instanceof Error\n ? networkError\n : new Error('Network error during token refresh');\n }\n\n if (!response.ok) {\n // Try to extract error message from response body\n let errorMessage = '';\n try {\n const body = await response.json();\n errorMessage = (body.message || body.error || '').toLowerCase();\n } catch {\n errorMessage = response.statusText.toLowerCase();\n }\n\n // Classify the error based on status + message\n // All 401s are fatal — no retry\n if (response.status === 401) {\n if (errorMessage.includes('expired')) {\n throw new SessionExpiredError('token_expired');\n }\n if (errorMessage.includes('invalid')) {\n throw new SessionExpiredError('token_invalid');\n }\n // Unknown 401 — treat as fatal\n throw new SessionExpiredError('token_invalid', `Unauthorized: ${errorMessage}`);\n }\n\n if (response.status === 400) {\n if (errorMessage.includes('inactive')) {\n throw new SessionExpiredError('user_inactive');\n }\n // Expired/invalid tokens returned as 400 — also fatal\n if (errorMessage.includes('expired') || errorMessage.includes('invalid')) {\n throw new SessionExpiredError('token_invalid', errorMessage);\n }\n // Other 400s — transient (\"User not found\", \"Token refresh failed: ...\")\n throw new Error(`Token refresh failed (400): ${errorMessage}`);\n }\n\n // 5xx or other — transient\n throw new Error(`Token refresh failed: ${response.status} ${errorMessage}`);\n }\n\n // Session may have been cleared (logout) while the fetch was in-flight.\n // Do NOT write tokens back to storage if that happened.\n if (this.sessionGeneration !== gen) {\n throw new SessionExpiredError('token_invalid', 'Session cleared during refresh');\n }\n\n const refreshResponse = await response.json();\n\n this.setTokens({\n accessToken: refreshResponse.accessToken,\n refreshToken: refreshResponse.refreshToken || refreshToken,\n expiresIn: refreshResponse.expiresIn,\n });\n }\n\n // --- Session expiry handler ---\n\n private handleSessionExpired(error: SessionExpiredError): void {\n this.cancelProactiveTimer();\n this.clearSession();\n\n if (this.onSessionExpired) {\n this.onSessionExpired(error);\n } else if (this.onRefreshFailed) {\n // Legacy callback fallback\n this.onRefreshFailed();\n }\n }\n\n // --- User data ---\n\n setUser(user: any): void {\n const currentData = this.tokenStorage.get() || {};\n this.tokenStorage.set({ ...currentData, user });\n }\n\n getUser(): any | null {\n const data = this.tokenStorage.get();\n return data?.user || null;\n }\n\n clearUser(): void {\n const currentData = this.tokenStorage.get() || {};\n delete currentData.user;\n this.tokenStorage.set(currentData);\n }\n\n // --- Session lifecycle ---\n\n clearSession(): void {\n this.sessionGeneration++;\n this.cancelProactiveTimer();\n // clearTokens removes the entire storage entry (tokens + user data)\n this.clearTokens();\n\n // Reject any pending queue entries\n const expiredError = new SessionExpiredError('token_invalid', 'Session cleared');\n this.rejectQueue(expiredError);\n }\n\n /**\n * Dispose of this SessionManager instance.\n * Cancels all timers and rejects pending queue entries.\n */\n destroy(): void {\n this.isDestroyed = true;\n // Remove from singleton registry\n SessionManager.instances.delete(this.storageKey);\n this.cancelProactiveTimer();\n const error = new SessionExpiredError('token_invalid', 'SessionManager destroyed');\n this.rejectQueue(error);\n }\n\n // --- JWT helpers ---\n\n /**\n * Decode JWT token and extract payload\n * Returns null if token is invalid or cannot be decoded\n */\n getTokenPayload(): JwtPayload | null {\n try {\n const tokens = this.getTokens();\n if (!tokens?.accessToken) return null;\n\n // JWT structure: header.payload.signature\n const parts = tokens.accessToken.split('.');\n if (parts.length !== 3) return null;\n\n // Decode base64 payload (second part)\n const payload = parts[1];\n const decodedPayload = atob(payload.replace(/-/g, '+').replace(/_/g, '/'));\n return JSON.parse(decodedPayload) as JwtPayload;\n } catch {\n // Silent fail - invalid token format\n return null;\n }\n }\n\n /**\n * Get userId from token (source of truth) or fallback to stored user\n */\n getUserId(): string | null {\n // Priority 1: Get from JWT token (source of truth)\n const payload = this.getTokenPayload();\n if (payload?.userId) return payload.userId;\n\n // Priority 2: Fallback to stored user data\n const user = this.getUser();\n return user?.id || null;\n }\n\n hasValidSession(): boolean {\n const tokens = this.getTokens();\n return tokens !== null && !this.isTokenExpired(tokens);\n }\n\n // --- Utility ---\n\n private sleep(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms));\n }\n}\n","import { HttpService } from './HttpService';\nimport type {\n LoginRequest,\n LoginResponse,\n SignupRequest,\n ChangePasswordRequest,\n RefreshTokenRequest,\n RefreshTokenResponse,\n MagicLinkRequest,\n MagicLinkResponse,\n VerifyMagicLinkRequest,\n VerifyMagicLinkResponse,\n ApiResponse,\n User,\n SwitchTenantRequest,\n SwitchTenantResponse,\n UserTenantMembership,\n} from '../types/api';\n\nexport class AuthApiService {\n constructor(private httpService: HttpService) {}\n\n // Public endpoints - no auth required\n async login(request: LoginRequest): Promise<LoginResponse> {\n const response = await this.httpService.post<LoginResponse>('/auth/login', request);\n return response;\n }\n\n async signup(request: SignupRequest): Promise<User> {\n const response = await this.httpService.post<User>('/auth/signup', request);\n return response;\n }\n\n async signupTenantAdmin(request: {\n email?: string;\n phoneNumber?: string;\n name: string;\n lastName?: string;\n password: string;\n tenantName: string;\n appId?: string;\n }): Promise<{ user: User; tenant: any }> {\n const response = await this.httpService.post<{ user: User; tenant: any }>(\n '/auth/signup/tenant-admin',\n request\n );\n return response;\n }\n\n async refreshToken(request: RefreshTokenRequest): Promise<RefreshTokenResponse> {\n const response = await this.httpService.post<RefreshTokenResponse>('/auth/refresh', request);\n return response;\n }\n\n async switchTenant(request: SwitchTenantRequest): Promise<SwitchTenantResponse> {\n const response = await this.httpService.post<SwitchTenantResponse>(\n '/auth/switch-tenant',\n request\n );\n return response;\n }\n\n async getUserTenants(authHeaders: Record<string, string>): Promise<UserTenantMembership[]> {\n const response = await this.httpService.get<UserTenantMembership[]>('/auth/tenants', {\n headers: authHeaders,\n });\n return response;\n }\n\n async requestPasswordReset(request: { email: string; tenantId: string }): Promise<void> {\n await this.httpService.post<void>('/auth/password-reset/request', request);\n }\n\n async sendMagicLink(request: MagicLinkRequest): Promise<MagicLinkResponse> {\n const response = await this.httpService.post<MagicLinkResponse>(\n '/auth/magic-link/send',\n request\n );\n return response;\n }\n\n async verifyMagicLink(request: VerifyMagicLinkRequest): Promise<VerifyMagicLinkResponse> {\n const response = await this.httpService.post<VerifyMagicLinkResponse>(\n '/auth/magic-link/verify',\n request\n );\n return response;\n }\n\n async confirmPasswordReset(request: { token: string; newPassword: string }): Promise<void> {\n await this.httpService.post<void>('/auth/password-reset/confirm', request);\n }\n\n // Protected endpoints - auth required\n async changePassword(\n request: ChangePasswordRequest,\n authHeaders: Record<string, string>\n ): Promise<void> {\n await this.httpService.post<ApiResponse<null>>('/auth/change-password', request, {\n headers: authHeaders,\n });\n }\n}\n","import { HttpService } from './HttpService';\nimport { SessionManager } from './SessionManager';\nimport type {\n Role,\n CreateRoleRequest,\n AssignRoleRequest,\n ApiResponse,\n PaginationParams,\n} from '../types/api';\n\nexport class RoleApiService {\n constructor(\n private httpService: HttpService,\n private sessionManager?: SessionManager\n ) {}\n\n async createRole(request: CreateRoleRequest): Promise<Role> {\n if (!this.sessionManager) {\n throw new Error('SessionManager is required for private endpoints');\n }\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const response = await this.httpService.post<ApiResponse<Role>>('/roles/', request, {\n headers: authHeaders,\n });\n return response.data;\n }\n\n async getRoleById(id: string): Promise<Role> {\n if (!this.sessionManager) {\n throw new Error('SessionManager is required for private endpoints');\n }\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const response = await this.httpService.get<ApiResponse<Role>>(`/roles/${id}`, {\n headers: authHeaders,\n });\n return response.data;\n }\n\n async updateRole(id: string, request: Partial<CreateRoleRequest>): Promise<Role> {\n if (!this.sessionManager) {\n throw new Error('SessionManager is required for private endpoints');\n }\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const response = await this.httpService.put<ApiResponse<Role>>(`/roles/${id}`, request, {\n headers: authHeaders,\n });\n return response.data;\n }\n\n async deleteRole(id: string): Promise<void> {\n if (!this.sessionManager) {\n throw new Error('SessionManager is required for private endpoints');\n }\n const authHeaders = await this.sessionManager.getAuthHeaders();\n await this.httpService.delete<void>(`/roles/${id}`, {\n headers: authHeaders,\n });\n }\n\n // Public endpoint - no auth required\n async getRolesByApp(\n appId: string,\n params?: PaginationParams\n ): Promise<{ roles: Role[]; meta: any }> {\n const queryParams = new URLSearchParams();\n\n if (params?.page) queryParams.append('page', params.page.toString());\n if (params?.limit) queryParams.append('limit', params.limit.toString());\n if (params?.sortBy) queryParams.append('sortBy', params.sortBy);\n if (params?.sortOrder) queryParams.append('sortOrder', params.sortOrder);\n\n const url = `/roles/app/${appId}${queryParams.toString() ? `?${queryParams.toString()}` : ''}`;\n const response = await this.httpService.get<ApiResponse<Role[]>>(url);\n\n return {\n roles: response.data,\n meta: response.meta,\n };\n }\n\n async assignRole(roleId: string, request: AssignRoleRequest): Promise<void> {\n if (!this.sessionManager) {\n throw new Error('SessionManager is required for private endpoints');\n }\n const authHeaders = await this.sessionManager.getAuthHeaders();\n await this.httpService.post<ApiResponse<null>>(`/roles/${roleId}/assign`, request, {\n headers: authHeaders,\n });\n }\n\n async revokeRole(roleId: string, request: AssignRoleRequest): Promise<void> {\n if (!this.sessionManager) {\n throw new Error('SessionManager is required for private endpoints');\n }\n const authHeaders = await this.sessionManager.getAuthHeaders();\n await this.httpService.post<ApiResponse<null>>(`/roles/${roleId}/revoke`, request, {\n headers: authHeaders,\n });\n }\n\n async getUserRoles(\n userId: string,\n params?: PaginationParams\n ): Promise<{ roles: Role[]; meta: any }> {\n if (!this.sessionManager) {\n throw new Error('SessionManager is required for private endpoints');\n }\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const queryParams = new URLSearchParams();\n\n if (params?.page) queryParams.append('page', params.page.toString());\n if (params?.limit) queryParams.append('limit', params.limit.toString());\n if (params?.sortBy) queryParams.append('sortBy', params.sortBy);\n if (params?.sortOrder) queryParams.append('sortOrder', params.sortOrder);\n\n const url = `/roles/user/${userId}${queryParams.toString() ? `?${queryParams.toString()}` : ''}`;\n const response = await this.httpService.get<ApiResponse<Role[]>>(url, {\n headers: authHeaders,\n });\n\n return {\n roles: response.data,\n meta: response.meta,\n };\n }\n}\n","import { HttpService } from './HttpService';\nimport { SessionManager } from './SessionManager';\nimport type { User, CreateUserRequest, ApiResponse, PaginationParams } from '../types/api';\n\nexport class UserApiService {\n constructor(\n private httpService: HttpService,\n private sessionManager: SessionManager\n ) {}\n\n async createUser(request: CreateUserRequest): Promise<User> {\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const response = await this.httpService.post<ApiResponse<User>>('/users/', request, {\n headers: authHeaders,\n });\n return response.data;\n }\n\n async getUsers(params?: PaginationParams): Promise<{ users: User[]; meta: any }> {\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const queryParams = new URLSearchParams();\n\n if (params?.page) queryParams.append('page', params.page.toString());\n if (params?.limit) queryParams.append('limit', params.limit.toString());\n if (params?.sortBy) queryParams.append('sortBy', params.sortBy);\n if (params?.sortOrder) queryParams.append('sortOrder', params.sortOrder);\n\n const url = `/users/${queryParams.toString() ? `?${queryParams.toString()}` : ''}`;\n const response = await this.httpService.get<ApiResponse<User[]>>(url, {\n headers: authHeaders,\n });\n\n return {\n users: response.data,\n meta: response.meta,\n };\n }\n\n async getUserById(id: string): Promise<User> {\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const response = await this.httpService.get<ApiResponse<User>>(`/users/${id}`, {\n headers: authHeaders,\n });\n return response.data;\n }\n\n async updateUser(id: string, request: Partial<CreateUserRequest>): Promise<User> {\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const response = await this.httpService.put<ApiResponse<User>>(`/users/${id}`, request, {\n headers: authHeaders,\n });\n return response.data;\n }\n\n async deleteUser(id: string): Promise<void> {\n const authHeaders = await this.sessionManager.getAuthHeaders();\n await this.httpService.delete<void>(`/users/${id}`, {\n headers: authHeaders,\n });\n }\n}\n","import { HttpService } from './HttpService';\nimport { SessionManager } from './SessionManager';\nimport type {\n Tenant,\n CreateTenantRequest,\n PublicTenantInfo,\n TenantSettings,\n UpdateTenantSettingsRequest,\n ApiResponse,\n PaginationParams,\n} from '../types/api';\n\nexport class TenantApiService {\n constructor(\n private httpService: HttpService,\n private appId: string,\n private sessionManager?: SessionManager\n ) {}\n\n async createTenant(request: CreateTenantRequest): Promise<Tenant> {\n if (!this.sessionManager) {\n throw new Error('SessionManager is required for private endpoints');\n }\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const response = await this.httpService.post<ApiResponse<Tenant>>('/tenants/', request, {\n headers: authHeaders,\n });\n return response.data;\n }\n\n async getTenants(params?: PaginationParams): Promise<{ tenants: Tenant[]; meta: any }> {\n if (!this.sessionManager) {\n throw new Error('SessionManager is required for private endpoints');\n }\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const queryParams = new URLSearchParams();\n\n if (params?.page) queryParams.append('page', params.page.toString());\n if (params?.limit) queryParams.append('limit', params.limit.toString());\n if (params?.sortBy) queryParams.append('sortBy', params.sortBy);\n if (params?.sortOrder) queryParams.append('sortOrder', params.sortOrder);\n\n const url = `/tenants/${queryParams.toString() ? `?${queryParams.toString()}` : ''}`;\n const response = await this.httpService.get<ApiResponse<Tenant[]>>(url, {\n headers: authHeaders,\n });\n\n return {\n tenants: response.data,\n meta: response.meta,\n };\n }\n\n async getTenantById(id: string): Promise<Tenant> {\n if (!this.sessionManager) {\n throw new Error('SessionManager is required for private endpoints');\n }\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const response = await this.httpService.get<ApiResponse<Tenant>>(`/tenants/${id}`, {\n headers: authHeaders,\n });\n return response.data;\n }\n\n async updateTenant(id: string, request: Partial<CreateTenantRequest>): Promise<Tenant> {\n if (!this.sessionManager) {\n throw new Error('SessionManager is required for private endpoints');\n }\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const response = await this.httpService.put<ApiResponse<Tenant>>(`/tenants/${id}`, request, {\n headers: authHeaders,\n });\n return response.data;\n }\n\n async adminUpdateTenant(id: string, request: Partial<CreateTenantRequest>): Promise<Tenant> {\n if (!this.sessionManager) {\n throw new Error('SessionManager is required for private endpoints');\n }\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const response = await this.httpService.put<ApiResponse<Tenant>>(\n `/tenants/${id}/admin-update`,\n request,\n {\n headers: authHeaders,\n }\n );\n return response.data;\n }\n\n // Public endpoint - no auth required\n async getPublicTenantInfo(slug: string): Promise<PublicTenantInfo> {\n const response = await this.httpService.get<ApiResponse<PublicTenantInfo>>(\n `/tenants/${this.appId}/${slug}/public`\n );\n return response.data;\n }\n\n // Settings endpoints\n async getTenantSettings(id: string): Promise<TenantSettings> {\n const response = await this.httpService.get<ApiResponse<TenantSettings>>(\n `/tenants/${id}/settings`\n );\n return response.data;\n }\n\n async updateTenantSettings(\n id: string,\n request: UpdateTenantSettingsRequest\n ): Promise<TenantSettings> {\n if (!this.sessionManager) {\n throw new Error('SessionManager is required for private endpoints');\n }\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const response = await this.httpService.put<ApiResponse<TenantSettings>>(\n `/tenants/${id}/settings`,\n request,\n {\n headers: authHeaders,\n }\n );\n return response.data;\n }\n}\n","/**\n * Tenant detection utilities\n * Extracted for testability\n */\n\nexport interface TenantDetectionConfig {\n tenantMode: 'subdomain' | 'selector' | 'fixed';\n baseDomain?: string;\n selectorParam?: string;\n fixedTenantSlug?: string;\n}\n\nexport interface LocationInfo {\n hostname: string;\n search: string;\n}\n\n/**\n * Detect tenant slug from subdomain\n */\nexport function detectSubdomainTenant(hostname: string, baseDomain?: string): string | null {\n // Skip localhost and IP addresses\n const isLocalhost =\n hostname === 'localhost' || hostname.startsWith('127.') || hostname.startsWith('192.168.');\n\n if (isLocalhost) {\n return null;\n }\n\n // If baseDomain is configured, use it to extract subdomain\n if (baseDomain) {\n const baseDomainLower = baseDomain.toLowerCase();\n const currentHost = hostname.toLowerCase();\n\n // Check if we're on the base domain (no subdomain)\n if (currentHost === baseDomainLower || currentHost === `www.${baseDomainLower}`) {\n return null;\n }\n\n // Check if hostname ends with baseDomain\n if (currentHost.endsWith(`.${baseDomainLower}`)) {\n const subdomain = currentHost.slice(0, -(baseDomainLower.length + 1));\n // Ignore www subdomain\n if (subdomain === 'www') {\n return null;\n }\n return subdomain;\n }\n\n // Hostname doesn't match baseDomain\n return null;\n }\n\n // Fallback: Extract subdomain assuming format subdomain.domain.tld\n const parts = hostname.split('.');\n if (parts.length >= 3 && parts[0] !== 'www') {\n return parts[0];\n }\n\n return null;\n}\n\n/**\n * Detect tenant slug from URL selector parameter\n */\nexport function detectSelectorTenant(\n search: string,\n selectorParam: string = 'tenant',\n localStorage?: Storage | null\n): string | null {\n const urlParams = new URLSearchParams(search);\n const urlTenant = urlParams.get(selectorParam);\n\n if (urlTenant) {\n // Save to localStorage when found in URL\n if (localStorage) {\n localStorage.setItem('tenant', urlTenant);\n }\n return urlTenant;\n }\n\n // Fallback to localStorage if not in URL\n if (localStorage) {\n return localStorage.getItem('tenant');\n }\n\n return null;\n}\n\n/**\n * Main tenant detection function\n */\nexport function detectTenantSlug(\n config: TenantDetectionConfig,\n location: LocationInfo,\n localStorage?: Storage | null\n): string | null {\n const { tenantMode, baseDomain, selectorParam, fixedTenantSlug } = config;\n\n if (tenantMode === 'fixed') {\n return fixedTenantSlug || null;\n }\n\n if (tenantMode === 'subdomain') {\n return detectSubdomainTenant(location.hostname, baseDomain);\n }\n\n if (tenantMode === 'selector') {\n return detectSelectorTenant(location.search, selectorParam, localStorage);\n }\n\n return null;\n}\n\n/**\n * Build the target hostname for tenant switching in subdomain mode\n * @param targetTenantSlug - The tenant slug to switch to\n * @param currentHostname - The current window.location.hostname\n * @param baseDomain - Optional configured base domain\n * @returns The new hostname or null if unable to determine\n */\nexport function buildTenantHostname(\n targetTenantSlug: string,\n currentHostname: string,\n baseDomain?: string\n): string | null {\n // If baseDomain is configured, use it directly (recommended)\n if (baseDomain) {\n return `${targetTenantSlug}.${baseDomain}`;\n }\n\n // Fallback: try to detect from current hostname\n const parts = currentHostname.split('.');\n\n if (parts.length === 2) {\n // Root domain (e.g., kommi.click) - ADD subdomain at the beginning\n // kommi.click → test-admin.kommi.click\n return `${targetTenantSlug}.${currentHostname}`;\n } else if (parts.length >= 3) {\n // Already has subdomain (e.g., old-tenant.kommi.click) - REPLACE first part\n // old-tenant.kommi.click → test-admin.kommi.click\n parts[0] = targetTenantSlug;\n return parts.join('.');\n }\n\n // Single-part hostname (e.g., localhost) - cannot determine\n return null;\n}\n","/**\n * Cross-domain authentication utilities\n * Used to transfer auth tokens between subdomains during tenant switching\n */\n\nexport interface AuthTokens {\n accessToken: string;\n refreshToken: string;\n expiresIn: number;\n}\n\nconst AUTH_TRANSFER_PARAM = '_auth';\n\n/**\n * Encode auth tokens for URL transfer\n * Uses base64 encoding for safe URL transport\n */\nexport function encodeAuthTokens(tokens: AuthTokens): string {\n const payload = JSON.stringify(tokens);\n // Use base64url encoding (URL-safe)\n return btoa(payload).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=/g, '');\n}\n\n/**\n * Decode auth tokens from URL parameter\n */\nexport function decodeAuthTokens(encoded: string): AuthTokens | null {\n try {\n // Restore base64 padding and characters\n let base64 = encoded.replace(/-/g, '+').replace(/_/g, '/');\n // Add padding if needed\n while (base64.length % 4) {\n base64 += '=';\n }\n const payload = atob(base64);\n const tokens = JSON.parse(payload);\n\n // Validate structure\n if (\n typeof tokens.accessToken === 'string' &&\n typeof tokens.refreshToken === 'string' &&\n typeof tokens.expiresIn === 'number'\n ) {\n return tokens;\n }\n return null;\n } catch {\n return null;\n }\n}\n\n/**\n * Extract auth tokens from current URL if present\n */\nexport function extractAuthTokensFromUrl(): AuthTokens | null {\n if (typeof window === 'undefined') {\n console.log('[CrossDomainAuth] SSR environment, skipping URL token extraction');\n return null;\n }\n\n const urlParams = new URLSearchParams(window.location.search);\n const encoded = urlParams.get(AUTH_TRANSFER_PARAM);\n\n console.log('[CrossDomainAuth] extractAuthTokensFromUrl called', {\n hasAuthParam: !!encoded,\n searchParams: window.location.search,\n encodedLength: encoded?.length,\n });\n\n if (!encoded) return null;\n\n const decoded = decodeAuthTokens(encoded);\n console.log('[CrossDomainAuth] Token decode result:', {\n success: !!decoded,\n hasAccessToken: !!decoded?.accessToken,\n hasRefreshToken: !!decoded?.refreshToken,\n expiresIn: decoded?.expiresIn,\n });\n\n return decoded;\n}\n\n/**\n * Remove auth tokens from URL without page reload\n */\nexport function clearAuthTokensFromUrl(): void {\n if (typeof window === 'undefined') return;\n\n const url = new URL(window.location.href);\n url.searchParams.delete(AUTH_TRANSFER_PARAM);\n\n console.log('[CrossDomainAuth] Clearing auth tokens from URL', {\n oldUrl: window.location.href,\n newUrl: url.toString(),\n });\n\n // Update URL without reload\n window.history.replaceState({}, '', url.toString());\n}\n\n/**\n * Build URL with auth tokens for cross-subdomain redirect\n */\nexport function buildUrlWithAuthTokens(baseUrl: string, tokens: AuthTokens, path?: string): string {\n const url = new URL(path || '/', baseUrl);\n url.searchParams.set(AUTH_TRANSFER_PARAM, encodeAuthTokens(tokens));\n return url.toString();\n}\n\n/**\n * Append auth tokens to an existing URL\n */\nexport function appendAuthTokensToUrl(url: string, tokens: AuthTokens): string {\n const urlObj = new URL(url);\n urlObj.searchParams.set(AUTH_TRANSFER_PARAM, encodeAuthTokens(tokens));\n return urlObj.toString();\n}\n\nexport { AUTH_TRANSFER_PARAM };\n","import {\n createContext,\n useContext,\n useMemo,\n ReactNode,\n useState,\n useEffect,\n useCallback,\n} from 'react';\nimport { useApp } from './AppProvider';\nimport { HttpService } from '../services/HttpService';\nimport { TenantApiService } from '../services/TenantApiService';\nimport { detectTenantSlug as detectTenant, buildTenantHostname } from '../utils/tenantDetection';\nimport { encodeAuthTokens, type AuthTokens, AUTH_TRANSFER_PARAM } from '../utils/crossDomainAuth';\nimport type { TenantSettings, JSONSchema, PublicTenantInfo } from '../types/api';\n\n// RFC-003: Cache interface for tenant info\ninterface CachedTenantInfo {\n data: PublicTenantInfo;\n timestamp: number;\n tenantSlug: string;\n}\n\nexport interface TenantConfig {\n // Tenant configuration\n tenantMode?: 'subdomain' | 'selector' | 'fixed';\n fixedTenantSlug?: string; // Required when tenantMode is 'fixed' — always uses this slug\n baseDomain?: string; // Base domain for subdomain mode (e.g., 'kommi.click')\n selectorParam?: string; // Default: 'tenant', used when tenantMode is 'selector'\n // RFC-003: Cache configuration\n cache?: {\n enabled?: boolean; // Default: true\n ttl?: number; // Time to live in milliseconds, default: 5 minutes\n storageKey?: string; // Default: 'tenant_cache_{tenantSlug}'\n };\n // SSR support\n initialTenant?: PublicTenantInfo;\n}\n\ninterface TenantContextValue {\n // Tenant info\n tenant: PublicTenantInfo | null;\n tenantSlug: string | null;\n isTenantLoading: boolean;\n tenantError: Error | null;\n retryTenant: () => void;\n // Settings\n settings: TenantSettings | null;\n settingsSchema: JSONSchema | null;\n isSettingsLoading: boolean;\n settingsError: Error | null;\n // Actions\n refreshSettings: () => void;\n switchTenant: (\n tenantSlug: string,\n options?: { mode?: 'navigate' | 'reload'; tokens?: AuthTokens; redirectPath?: string }\n ) => void;\n // Validation\n validateSettings: (settings: TenantSettings) => { isValid: boolean; errors: string[] };\n}\n\nconst TenantContext = createContext<TenantContextValue | null>(null);\n\ninterface TenantProviderProps {\n config: TenantConfig;\n children: ReactNode;\n}\n\nexport function TenantProvider({ config, children }: TenantProviderProps) {\n const { baseUrl, appInfo, appId } = useApp();\n\n // Detect tenant slug from URL using extracted utility\n const detectTenantSlug = useCallback((): string | null => {\n if (typeof window === 'undefined') return null;\n\n return detectTenant(\n {\n tenantMode: config.tenantMode || 'selector',\n baseDomain: config.baseDomain,\n selectorParam: config.selectorParam,\n fixedTenantSlug: config.fixedTenantSlug,\n },\n {\n hostname: window.location.hostname,\n search: window.location.search,\n },\n window.localStorage\n );\n }, [config.tenantMode, config.baseDomain, config.selectorParam, config.fixedTenantSlug]);\n\n // Detect tenant slug on mount and on URL changes\n const [tenantSlug, setTenantSlug] = useState<string | null>(() => detectTenantSlug());\n\n // RFC-003: Cache configuration with defaults\n const cacheConfig = useMemo(\n () => ({\n enabled: config.cache?.enabled ?? true,\n ttl: config.cache?.ttl ?? 5 * 60 * 1000, // 5 minutes default\n storageKey: config.cache?.storageKey ?? `tenant_cache_${tenantSlug || 'default'}`,\n }),\n [config.cache, tenantSlug]\n );\n\n // RFC-003: Try to load from cache on initialization\n const [tenant, setTenant] = useState<PublicTenantInfo | null>(() => {\n if (config.initialTenant) return config.initialTenant;\n if (!cacheConfig.enabled || !tenantSlug) return null;\n\n try {\n const cached = localStorage.getItem(cacheConfig.storageKey);\n if (!cached) return null;\n\n const parsed: CachedTenantInfo = JSON.parse(cached);\n const now = Date.now();\n const age = now - parsed.timestamp;\n\n // Check if cache is still valid\n if (age < cacheConfig.ttl && parsed.tenantSlug === tenantSlug) {\n return parsed.data;\n }\n\n // Cache expired\n localStorage.removeItem(cacheConfig.storageKey);\n return null;\n } catch {\n return null;\n }\n });\n\n const [isTenantLoading, setIsTenantLoading] = useState(!tenant && !config.initialTenant);\n const [tenantError, setTenantError] = useState<Error | null>(null);\n\n // Settings state\n const [settings, setSettings] = useState<TenantSettings | null>(null);\n const [isSettingsLoading, setIsSettingsLoading] = useState(false);\n const [settingsError, setSettingsError] = useState<Error | null>(null);\n\n // Re-detect tenant slug when URL changes (skip in fixed mode — slug never changes)\n useEffect(() => {\n if (config.tenantMode === 'fixed') return;\n const detected = detectTenantSlug();\n setTenantSlug(detected);\n }, [detectTenantSlug, config.tenantMode]);\n\n // Get settings schema from app info\n const settingsSchema = appInfo?.settingsSchema || null;\n\n // RFC-003: Load tenant info with caching\n const loadTenant = useCallback(\n async (slug: string, bypassCache = false) => {\n // Check cache first (unless bypassing)\n if (!bypassCache && cacheConfig.enabled && tenant && tenant.domain === slug) {\n return; // Already have valid cached data\n }\n\n try {\n setIsTenantLoading(true);\n setTenantError(null);\n\n const httpService = new HttpService(baseUrl);\n const tenantApi = new TenantApiService(httpService, appId);\n const tenantInfo = await tenantApi.getPublicTenantInfo(slug);\n setTenant(tenantInfo);\n\n // RFC-003: Save to cache\n if (cacheConfig.enabled) {\n try {\n const cacheData: CachedTenantInfo = {\n data: tenantInfo,\n timestamp: Date.now(),\n tenantSlug: slug,\n };\n localStorage.setItem(cacheConfig.storageKey, JSON.stringify(cacheData));\n } catch (error) {\n console.warn('Failed to cache tenant info:', error);\n }\n }\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to load tenant information');\n setTenantError(error);\n setTenant(null);\n } finally {\n setIsTenantLoading(false);\n }\n },\n [baseUrl, appId, cacheConfig, tenant]\n );\n\n // RFC-003: Background refresh for stale-while-revalidate\n const backgroundRefresh = useCallback(async () => {\n if (!cacheConfig.enabled || !tenant || !tenantSlug) return;\n\n try {\n const cached = localStorage.getItem(cacheConfig.storageKey);\n if (!cached) return;\n\n const parsed: CachedTenantInfo = JSON.parse(cached);\n const age = Date.now() - parsed.timestamp;\n\n // If cache is more than 50% expired, refresh in background\n if (age > cacheConfig.ttl * 0.5) {\n const httpService = new HttpService(baseUrl);\n const tenantApi = new TenantApiService(httpService, appId);\n const tenantInfo = await tenantApi.getPublicTenantInfo(tenantSlug);\n\n setTenant(tenantInfo);\n\n const cacheData: CachedTenantInfo = {\n data: tenantInfo,\n timestamp: Date.now(),\n tenantSlug,\n };\n localStorage.setItem(cacheConfig.storageKey, JSON.stringify(cacheData));\n }\n } catch (error) {\n console.warn('Background tenant refresh failed:', error);\n // Don't update error state - keep showing cached data\n }\n }, [baseUrl, appId, cacheConfig, tenant, tenantSlug]);\n\n // Load tenant settings\n const loadSettings = useCallback(async () => {\n if (!tenant?.id) return;\n\n try {\n setIsSettingsLoading(true);\n setSettingsError(null);\n\n const httpService = new HttpService(baseUrl);\n const tenantApi = new TenantApiService(httpService, tenant.appId);\n const tenantSettings = await tenantApi.getTenantSettings(tenant.id);\n setSettings(tenantSettings);\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to load tenant settings');\n setSettingsError(error);\n setSettings(null);\n } finally {\n setIsSettingsLoading(false);\n }\n }, [baseUrl, tenant]);\n\n // Refresh settings\n const refreshSettings = useCallback(() => {\n loadSettings();\n }, [loadSettings]);\n\n // Validate settings against schema\n const validateSettings = useCallback(\n (settingsToValidate: TenantSettings) => {\n if (!settingsSchema) {\n return { isValid: true, errors: [] };\n }\n\n const errors: string[] = [];\n\n try {\n // If settingsSchema has properties, validate against them\n if (settingsSchema.properties) {\n Object.entries(settingsSchema.properties).forEach(([key, fieldSchema]) => {\n const value = settingsToValidate[key];\n\n // Check required fields\n if (settingsSchema.required?.includes(key) && (value === undefined || value === null)) {\n errors.push(`Field '${key}' is required`);\n return;\n }\n\n // Skip validation if value is not provided and not required\n if (value === undefined || value === null) return;\n\n // Type validation using JSONSchema\n if (fieldSchema.type) {\n const expectedType = fieldSchema.type;\n const actualType = typeof value;\n\n if (expectedType === 'string' && actualType !== 'string') {\n errors.push(`Field '${key}' must be a string`);\n } else if (\n (expectedType === 'number' || expectedType === 'integer') &&\n actualType !== 'number'\n ) {\n errors.push(`Field '${key}' must be a number`);\n } else if (expectedType === 'boolean' && actualType !== 'boolean') {\n errors.push(`Field '${key}' must be a boolean`);\n } else if (expectedType === 'array' && !Array.isArray(value)) {\n errors.push(`Field '${key}' must be an array`);\n }\n }\n\n // String length validation\n if (\n fieldSchema.minLength !== undefined &&\n typeof value === 'string' &&\n value.length < fieldSchema.minLength\n ) {\n errors.push(\n `Field '${key}' must be at least ${fieldSchema.minLength} characters long`\n );\n }\n if (\n fieldSchema.maxLength !== undefined &&\n typeof value === 'string' &&\n value.length > fieldSchema.maxLength\n ) {\n errors.push(\n `Field '${key}' must be no more than ${fieldSchema.maxLength} characters long`\n );\n }\n\n // Number range validation\n if (\n fieldSchema.minimum !== undefined &&\n typeof value === 'number' &&\n value < fieldSchema.minimum\n ) {\n errors.push(`Field '${key}' must be at least ${fieldSchema.minimum}`);\n }\n if (\n fieldSchema.maximum !== undefined &&\n typeof value === 'number' &&\n value > fieldSchema.maximum\n ) {\n errors.push(`Field '${key}' must be no more than ${fieldSchema.maximum}`);\n }\n\n // Pattern validation for strings\n if (fieldSchema.pattern && typeof value === 'string') {\n const regex = new RegExp(fieldSchema.pattern);\n if (!regex.test(value)) {\n errors.push(`Field '${key}' does not match the required pattern`);\n }\n }\n\n // Enum validation\n if (fieldSchema.enum && !fieldSchema.enum.includes(value)) {\n errors.push(`Field '${key}' must be one of: ${fieldSchema.enum.join(', ')}`);\n }\n });\n }\n\n return {\n isValid: errors.length === 0,\n errors,\n };\n } catch {\n return {\n isValid: false,\n errors: ['Invalid settings schema or validation error'],\n };\n }\n },\n [settingsSchema]\n );\n\n // RFC-003: Load tenant on mount or do background refresh\n useEffect(() => {\n if (!config.initialTenant && tenantSlug) {\n if (!tenant) {\n // No cached data, fetch from server\n loadTenant(tenantSlug);\n } else {\n // We have cached data, do background refresh\n backgroundRefresh();\n }\n } else if (!config.initialTenant && !tenantSlug) {\n // No tenant slug found - continue without tenant\n setTenant(null);\n setTenantError(null);\n setIsTenantLoading(false);\n }\n }, [config.initialTenant, tenantSlug, tenant, loadTenant, backgroundRefresh]);\n\n // Load settings when tenant changes\n useEffect(() => {\n if (tenant?.id) {\n loadSettings();\n } else {\n setSettings(null);\n setSettingsError(null);\n setIsSettingsLoading(false);\n }\n }, [tenant?.id, loadSettings]);\n\n // Switch tenant by updating URL and reloading page\n const switchTenant = useCallback(\n (\n targetTenantSlug: string,\n options?: { mode?: 'navigate' | 'reload'; tokens?: AuthTokens; redirectPath?: string }\n ) => {\n const { mode = 'reload', tokens, redirectPath } = options || {};\n const tenantMode = config.tenantMode || 'selector';\n\n // Fixed mode: switching tenants is not supported\n if (tenantMode === 'fixed') {\n console.warn(\n '[TenantProvider] switchTenant is a no-op in fixed mode. Tenant is always:',\n config.fixedTenantSlug\n );\n // Still navigate to redirectPath if provided\n if (redirectPath) {\n window.location.href = redirectPath;\n }\n return;\n }\n\n // Update localStorage first\n localStorage.setItem('tenant', targetTenantSlug);\n\n if (tenantMode === 'subdomain') {\n // Subdomain mode: redirect to new subdomain\n const currentHostname = window.location.hostname;\n const newHostname = buildTenantHostname(\n targetTenantSlug,\n currentHostname,\n config.baseDomain\n );\n\n if (!newHostname) {\n console.warn(\n '[TenantProvider] Cannot switch subdomain, invalid hostname:',\n currentHostname\n );\n return;\n }\n\n // Build the new URL\n const targetPath = redirectPath || window.location.pathname;\n const url = new URL(`${window.location.protocol}//${newHostname}${targetPath}`);\n\n // Copy existing search params (except auth transfer)\n const currentParams = new URLSearchParams(window.location.search);\n currentParams.forEach((value, key) => {\n if (key !== AUTH_TRANSFER_PARAM) {\n url.searchParams.set(key, value);\n }\n });\n\n // If tokens provided, encode and add to URL for cross-subdomain auth\n if (tokens) {\n url.searchParams.set(AUTH_TRANSFER_PARAM, encodeAuthTokens(tokens));\n }\n\n window.location.href = url.toString();\n } else if (tenantMode === 'selector') {\n // Selector mode: update URL parameter\n const targetPath = redirectPath || window.location.pathname;\n const urlParams = new URLSearchParams(window.location.search);\n urlParams.set(config.selectorParam || 'tenant', targetTenantSlug);\n\n // Remove existing auth transfer param if present\n urlParams.delete(AUTH_TRANSFER_PARAM);\n\n // If tokens provided, encode and add to URL (same as subdomain mode)\n if (tokens) {\n urlParams.set(AUTH_TRANSFER_PARAM, encodeAuthTokens(tokens));\n }\n\n if (mode === 'reload') {\n // Full page reload with new tenant\n const newUrl = `${targetPath}?${urlParams.toString()}${window.location.hash}`;\n window.location.href = newUrl;\n } else {\n // Navigate without reload (requires router integration)\n const newUrl = `${targetPath}?${urlParams.toString()}${window.location.hash}`;\n window.history.pushState({}, '', newUrl);\n // Update state to trigger re-render\n setTenantSlug(targetTenantSlug);\n // Trigger tenant reload\n loadTenant(targetTenantSlug);\n }\n }\n },\n [config.tenantMode, config.selectorParam, config.baseDomain, loadTenant]\n );\n\n const contextValue = useMemo(() => {\n // Retry function for tenant loading\n const retryTenant = () => {\n if (tenantSlug) {\n loadTenant(tenantSlug);\n }\n };\n\n return {\n // Tenant info\n tenant,\n tenantSlug,\n isTenantLoading,\n tenantError,\n retryTenant,\n // Settings\n settings,\n settingsSchema,\n isSettingsLoading,\n settingsError,\n // Actions\n refreshSettings,\n switchTenant,\n // Validation\n validateSettings,\n };\n }, [\n tenant,\n tenantSlug,\n isTenantLoading,\n tenantError,\n settings,\n settingsSchema,\n isSettingsLoading,\n settingsError,\n refreshSettings,\n switchTenant,\n validateSettings,\n ]);\n\n // No longer blocks children - loading state is exposed via context\n // Use AppLoader component to block until ready\n return <TenantContext.Provider value={contextValue}>{children}</TenantContext.Provider>;\n}\n\nexport function useTenant(): TenantContextValue {\n const context = useContext(TenantContext);\n if (!context) {\n throw new Error('useTenant must be used within a TenantProvider');\n }\n return context;\n}\n\n// Optional hook that returns null if not inside TenantProvider\nexport function useTenantOptional(): TenantContextValue | null {\n return useContext(TenantContext);\n}\n\n// Backward compatibility\nexport const useTenantSettings = useTenant;\n\n// Convenience hook for just the settings\nexport function useSettings() {\n const { settings, settingsSchema, isSettingsLoading, settingsError, validateSettings } =\n useTenant();\n return {\n settings,\n settingsSchema,\n isLoading: isSettingsLoading,\n error: settingsError,\n validateSettings,\n };\n}\n\n// Convenience hook for just tenant info\nexport function useTenantInfo() {\n const { tenant, tenantSlug, isTenantLoading, tenantError, retryTenant } = useTenant();\n return {\n tenant,\n tenantSlug,\n isLoading: isTenantLoading,\n error: tenantError,\n retry: retryTenant,\n };\n}\n","import { createContext, useContext, ReactNode, useMemo, useState, useEffect, useRef } from 'react';\nimport { SessionManager } from '../services/SessionManager';\nimport { AuthApiService } from '../services/AuthApiService';\nimport { RoleApiService } from '../services/RoleApiService';\nimport { UserApiService } from '../services/UserApiService';\nimport { TenantApiService } from '../services/TenantApiService';\nimport { HttpService } from '../services/HttpService';\nimport { useApp } from './AppProvider';\nimport { useTenant } from './TenantProvider';\nimport { extractAuthTokensFromUrl, clearAuthTokensFromUrl } from '../utils/crossDomainAuth';\nimport { SessionExpiredError } from '../errors/SessionErrors';\nimport type {\n Role,\n Permission,\n User,\n LoginResponse,\n VerifyMagicLinkResponse,\n MagicLinkResponse,\n UserTenantMembership,\n} from '../types/api';\nimport type {\n LoginParams,\n SignupParams,\n SignupTenantAdminParams,\n SendMagicLinkParams,\n VerifyMagicLinkParams,\n RequestPasswordResetParams,\n ConfirmPasswordResetParams,\n ChangePasswordParams,\n} from '../types/authParams';\n\nexport interface AuthConfig {\n /** @deprecated Use onSessionExpired instead */\n onRefreshFailed?: () => void;\n onSessionExpired?: (error: SessionExpiredError) => void;\n initialRoles?: Role[]; // For SSR injection\n // Session config\n refreshQueueTimeout?: number; // ms before queued requests timeout (default: 10000)\n proactiveRefreshMargin?: number; // ms before expiry to trigger proactive refresh (default: 60000)\n // Multi-tenant options (RFC-004)\n autoSwitchSingleTenant?: boolean; // Auto-switch if user has only one tenant (default: true)\n onTenantSelectionRequired?: (tenants: UserTenantMembership[]) => void; // Callback when user needs to select tenant\n}\n\nexport interface AuthContextValue {\n // RFC-003: Authentication state\n isAuthenticated: boolean;\n sessionManager: SessionManager;\n authenticatedHttpService: HttpService; // Authenticated HttpService for protected endpoints\n // Auth methods (RFC-002: Object parameters)\n login: (params: LoginParams) => Promise<LoginResponse>;\n signup: (params: SignupParams) => Promise<User>;\n signupTenantAdmin: (params: SignupTenantAdminParams) => Promise<{ user: User; tenant: any }>;\n // Magic Link methods\n sendMagicLink: (params: SendMagicLinkParams) => Promise<MagicLinkResponse>;\n verifyMagicLink: (params: VerifyMagicLinkParams) => Promise<VerifyMagicLinkResponse>;\n changePassword: (params: ChangePasswordParams) => Promise<void>;\n requestPasswordReset: (params: RequestPasswordResetParams) => Promise<void>;\n confirmPasswordReset: (params: ConfirmPasswordResetParams) => Promise<void>;\n refreshToken: () => Promise<void>;\n logout: () => void;\n // Session methods\n setTokens: (tokens: { accessToken: string; refreshToken: string; expiresIn: number }) => void;\n hasValidSession: () => boolean;\n clearSession: () => void;\n // User data\n currentUser: User | null;\n isUserLoading: boolean;\n userError: Error | null;\n loadUserData: (forceRefresh?: boolean) => Promise<void>;\n refreshUser: () => Promise<void>;\n // Initialization state (for cross-subdomain auth)\n isAuthInitializing: boolean;\n isAuthReady: boolean;\n // Role and Permission methods\n userRole: Role | null;\n userPermissions: string[];\n availableRoles: Role[];\n rolesLoading: boolean;\n hasPermission: (permission: string | Permission) => boolean;\n hasAnyPermission: (permissions: (string | Permission)[]) => boolean;\n hasAllPermissions: (permissions: (string | Permission)[]) => boolean;\n getUserPermissionStrings: () => string[];\n refreshRoles: () => Promise<void>;\n // RFC-004: Multi-tenant user membership\n userTenants: UserTenantMembership[];\n hasTenantContext: boolean;\n switchToTenant: (tenantId: string, options?: { redirectPath?: string }) => Promise<void>;\n refreshUserTenants: () => Promise<UserTenantMembership[]>;\n}\n\nconst AuthContext = createContext<AuthContextValue | null>(null);\n\ninterface AuthProviderProps {\n config?: AuthConfig;\n children: ReactNode;\n}\n\nexport function AuthProvider({ config = {}, children }: AuthProviderProps) {\n const { appId, baseUrl } = useApp();\n const { tenant, tenantSlug, switchTenant } = useTenant();\n const [availableRoles, setAvailableRoles] = useState<Role[]>(config.initialRoles || []);\n const [rolesLoading, setRolesLoading] = useState(!config.initialRoles);\n const [currentUser, setCurrentUser] = useState<User | null>(null);\n const [isUserLoading, setIsUserLoading] = useState(false);\n const [userError, setUserError] = useState<Error | null>(null);\n\n // RFC-004: Multi-tenant user membership state\n const [userTenants, setUserTenants] = useState<UserTenantMembership[]>(() => {\n // Try to load cached tenants from localStorage\n try {\n const cached = localStorage.getItem('userTenants');\n return cached ? JSON.parse(cached) : [];\n } catch {\n return [];\n }\n });\n const [hasTenantContext, setHasTenantContext] = useState<boolean>(false);\n\n // === SYNCHRONOUS INITIALIZATION ===\n // Process URL tokens and localStorage BEFORE first render completes\n // This ensures guards see valid session immediately\n const initRef = useRef<{\n done: boolean;\n urlTokens: ReturnType<typeof extractAuthTokensFromUrl>;\n }>({ done: false, urlTokens: null });\n\n // Extract URL tokens synchronously on first render\n if (!initRef.current.done) {\n initRef.current.done = true;\n initRef.current.urlTokens = extractAuthTokensFromUrl();\n if (initRef.current.urlTokens) {\n console.log(\n '[AuthProvider] SYNC: URL tokens found, will block isAuthReady until user loaded'\n );\n }\n }\n\n // Track if we're loading user after URL token consumption\n // CRITICAL: Initialize to TRUE if we have URL tokens, so isAuthReady stays false until user is loaded\n const [isLoadingAfterUrlTokens, setIsLoadingAfterUrlTokens] = useState(() => {\n const hasUrlTokens = initRef.current.urlTokens !== null;\n console.log('[AuthProvider] SYNC: isLoadingAfterUrlTokens initial:', hasUrlTokens);\n return hasUrlTokens;\n });\n\n // Create services with stable references — singleton per tenantSlug\n const sessionManager = useMemo(() => {\n const manager = SessionManager.getInstance({\n tenantSlug: tenantSlug,\n baseUrl: baseUrl,\n refreshQueueTimeout: config.refreshQueueTimeout,\n proactiveRefreshMargin: config.proactiveRefreshMargin,\n onSessionExpired: (error: SessionExpiredError) => {\n // Clear React auth state when session expires\n setCurrentUser(null);\n setUserError(null);\n setUserTenants([]);\n setHasTenantContext(false);\n try {\n localStorage.removeItem('userTenants');\n } catch {\n // Ignore localStorage errors\n }\n // Call user callbacks\n if (config.onSessionExpired) {\n config.onSessionExpired(error);\n } else if (config.onRefreshFailed) {\n config.onRefreshFailed();\n }\n },\n });\n\n // If we have URL tokens, save them immediately (sync)\n if (initRef.current.urlTokens) {\n console.log('[AuthProvider] SYNC: Saving URL tokens to session manager');\n manager.setTokens({\n accessToken: initRef.current.urlTokens.accessToken,\n refreshToken: initRef.current.urlTokens.refreshToken,\n expiresIn: initRef.current.urlTokens.expiresIn,\n });\n console.log('[AuthProvider] SYNC: Session valid:', manager.hasValidSession());\n }\n\n return manager;\n }, [tenantSlug, baseUrl, config.refreshQueueTimeout, config.proactiveRefreshMargin]);\n\n // Track if we're restoring an existing session (tokens in localStorage but user not loaded yet)\n // CRITICAL: Initialize to TRUE if there's a valid session OR if tokens are expired but\n // refreshable (backgroundRefresh is pending). This keeps isAuthReady false until\n // the refresh attempt settles, preventing premature redirects to login.\n const [isRestoringSession, setIsRestoringSession] = useState(() => {\n if (initRef.current.urlTokens) return false; // URL tokens path handles its own blocking\n const tokens = sessionManager.getTokens();\n if (!tokens) return false;\n // Valid session (need to load user) OR expired but has refreshToken (refresh pending)\n return sessionManager.hasValidSession() || !!tokens.refreshToken;\n });\n\n // Auth is ready when:\n // 1. Initial token check is done AND\n // 2. If we had URL tokens, user data must be loaded (not loading) AND\n // 3. If we had an existing session, user data must be restored\n const isAuthReady = initRef.current.done && !isLoadingAfterUrlTokens && !isRestoringSession;\n\n const authenticatedHttpService = useMemo(() => {\n const service = new HttpService(baseUrl);\n service.setSessionManager(sessionManager);\n return service;\n }, [baseUrl, sessionManager]);\n\n const authApiService = useMemo(() => {\n return new AuthApiService(new HttpService(baseUrl));\n }, [baseUrl]);\n\n const userApiService = useMemo(() => {\n return new UserApiService(authenticatedHttpService, sessionManager);\n }, [authenticatedHttpService, sessionManager]);\n\n const roleApiService = useMemo(() => {\n return new RoleApiService(new HttpService(baseUrl));\n }, [baseUrl]);\n\n // Calculate derived values with useMemo to prevent recalculation\n const user = useMemo(() => {\n return currentUser || sessionManager.getUser();\n }, [currentUser, sessionManager]);\n\n const userRole = useMemo(() => {\n return user?.roleId ? availableRoles.find(role => role.id === user.roleId) || null : null;\n }, [user, availableRoles]);\n\n const userPermissions = useMemo(() => {\n const permissions = userRole?.permissions || [];\n // Permissions from API are already strings in 'resource.action' format\n return permissions;\n }, [userRole]);\n\n // RFC-003: Compute isAuthenticated from session state\n const isAuthenticated = useMemo(() => {\n return sessionManager.hasValidSession() && currentUser !== null;\n }, [sessionManager, currentUser]);\n\n // Stable ref so effects can call loadUserData without depending on contextValue\n const loadUserDataRef = useRef<(forceRefresh?: boolean) => Promise<void>>(async () => {});\n\n const contextValue = useMemo(() => {\n // Load user data from API with cache control\n const loadUserData = async (forceRefresh = false) => {\n try {\n // 1. Check if we have a valid session\n if (!sessionManager.hasValidSession()) {\n return;\n }\n\n // 2. Skip if we already have user data (unless forced)\n if (!forceRefresh && currentUser) {\n return;\n }\n\n // 3. Get userId from token (source of truth) or fallback to stored user\n const userId = sessionManager.getUserId();\n if (!userId) {\n console.warn('[AuthProvider] No userId available in token or storage');\n return;\n }\n\n setIsUserLoading(true);\n setUserError(null);\n\n const userData = await userApiService.getUserById(userId);\n setCurrentUser(userData);\n sessionManager.setUser(userData); // Update session with fresh data\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to load user data');\n setUserError(error);\n console.error('[AuthProvider] Failed to load user data:', error);\n } finally {\n setIsUserLoading(false);\n }\n };\n\n const refreshUser = async () => {\n await loadUserData();\n };\n\n // Auth methods (RFC-002: Object parameters + RFC-001: Auto-switch + RFC-004: Multi-tenant)\n const login = async (params: LoginParams): Promise<LoginResponse> => {\n const { username, password, tenantSlug: targetSlug, redirectPath } = params;\n\n // RFC-001: Get tenantId from slug if provided, otherwise use current context\n let resolvedTenantId = tenant?.id;\n let targetTenantSlug = tenantSlug;\n let targetSessionManager = sessionManager;\n\n if (targetSlug) {\n // Get tenant ID from public endpoint using slug\n const tenantApi = new TenantApiService(authenticatedHttpService, appId);\n const tenantInfo = await tenantApi.getPublicTenantInfo(targetSlug);\n resolvedTenantId = tenantInfo.id;\n targetTenantSlug = targetSlug;\n }\n\n const loginResponse = await authApiService.login({\n username,\n password,\n appId,\n tenantId: resolvedTenantId,\n });\n\n // Check if we need to switch\n const shouldSwitch = targetSlug && targetSlug !== tenantSlug;\n\n // Save tokens to the correct tenant\n\n if (shouldSwitch) {\n // If switching, create a new SessionManager for the target tenant\n targetSessionManager = new SessionManager({\n tenantSlug: targetTenantSlug,\n baseUrl: baseUrl,\n });\n }\n\n targetSessionManager.setTokens({\n accessToken: loginResponse.accessToken,\n refreshToken: loginResponse.refreshToken,\n expiresIn: loginResponse.expiresIn,\n });\n\n // Store user data if available in the response\n if (loginResponse.user) {\n targetSessionManager.setUser(loginResponse.user);\n setCurrentUser(loginResponse.user);\n\n // Load complete user data from API after login\n try {\n await loadUserData();\n } catch (error) {\n console.warn('Failed to load complete user data after login:', error);\n }\n }\n\n // RFC-004: Store user tenants from login response\n if (loginResponse.tenants && loginResponse.tenants.length > 0) {\n setUserTenants(loginResponse.tenants);\n // Cache in localStorage\n try {\n localStorage.setItem('userTenants', JSON.stringify(loginResponse.tenants));\n } catch {\n // Ignore localStorage errors\n }\n }\n\n // RFC-004: Determine if we have tenant context\n const hasTenant = loginResponse.user?.tenantId !== null;\n setHasTenantContext(hasTenant);\n\n // Build tokens object for redirect\n const tokens = {\n accessToken: loginResponse.accessToken,\n refreshToken: loginResponse.refreshToken,\n expiresIn: loginResponse.expiresIn,\n };\n\n // Handle navigation after login\n if (shouldSwitch && targetTenantSlug) {\n // Switching to different tenant - use switchTenant for cross-subdomain auth\n switchTenant(targetTenantSlug, { tokens, redirectPath });\n return loginResponse; // Code after this won't execute due to page reload\n }\n\n // Same tenant or no tenant switch - navigate to redirectPath if provided\n if (redirectPath && redirectPath !== window.location.pathname) {\n // Use switchTenant even for same tenant to handle redirect consistently\n switchTenant(targetTenantSlug || tenantSlug || '', { tokens, redirectPath });\n return loginResponse;\n }\n\n // RFC-004: Handle global login (no tenantId) - auto-switch or callback\n if (!hasTenant && loginResponse.tenants && loginResponse.tenants.length > 0) {\n const autoSwitch = params.autoSwitch !== false && config.autoSwitchSingleTenant !== false; // default true\n\n if (loginResponse.tenants.length === 1 && autoSwitch) {\n // Auto-switch to the only tenant\n const singleTenant = loginResponse.tenants[0];\n switchTenant(singleTenant.subdomain, { tokens, redirectPath });\n return loginResponse;\n } else if (loginResponse.tenants.length > 1 && config.onTenantSelectionRequired) {\n // Multiple tenants - trigger callback for tenant selection\n config.onTenantSelectionRequired(loginResponse.tenants);\n }\n }\n\n return loginResponse;\n };\n\n const signup = async (params: SignupParams): Promise<User> => {\n const { email, phoneNumber, name, password, lastName, tenantId } = params;\n\n if (!email && !phoneNumber) {\n throw new Error('Either email or phoneNumber is required');\n }\n if (!name || !password) {\n throw new Error('Name and password are required');\n }\n\n const resolvedTenantId = tenantId ?? tenant?.id;\n\n const signupResponse = await authApiService.signup({\n email,\n phoneNumber,\n name,\n password,\n tenantId: resolvedTenantId,\n lastName,\n appId,\n });\n return signupResponse;\n };\n\n const signupTenantAdmin = async (\n params: SignupTenantAdminParams\n ): Promise<{ user: User; tenant: any }> => {\n const { email, phoneNumber, name, password, tenantName, lastName } = params;\n\n if (!email && !phoneNumber) {\n throw new Error('Either email or phoneNumber is required');\n }\n if (!name || !password || !tenantName) {\n throw new Error('Name, password, and tenantName are required');\n }\n\n const signupResponse = await authApiService.signupTenantAdmin({\n email,\n phoneNumber,\n name,\n password,\n tenantName,\n appId,\n lastName,\n });\n return signupResponse;\n };\n\n const changePassword = async (params: ChangePasswordParams): Promise<void> => {\n const { currentPassword, newPassword } = params;\n const authHeaders = await sessionManager.getAuthHeaders();\n await authApiService.changePassword({ currentPassword, newPassword }, authHeaders);\n };\n\n const requestPasswordReset = async (params: RequestPasswordResetParams): Promise<void> => {\n const { email, tenantId } = params;\n const resolvedTenantId = tenantId ?? tenant?.id;\n\n if (!resolvedTenantId) {\n throw new Error('tenantId is required for password reset');\n }\n\n await authApiService.requestPasswordReset({ email, tenantId: resolvedTenantId });\n };\n\n const confirmPasswordReset = async (params: ConfirmPasswordResetParams): Promise<void> => {\n const { token, newPassword } = params;\n await authApiService.confirmPasswordReset({ token, newPassword });\n };\n\n // Magic Link methods\n const sendMagicLink = async (params: SendMagicLinkParams): Promise<MagicLinkResponse> => {\n const { email, frontendUrl, name, lastName, tenantId } = params;\n const resolvedTenantId = tenantId ?? tenant?.id;\n\n if (!resolvedTenantId) {\n throw new Error('tenantId is required for magic link authentication');\n }\n\n const response = await authApiService.sendMagicLink({\n email,\n tenantId: resolvedTenantId,\n frontendUrl,\n name,\n lastName,\n appId,\n });\n return response;\n };\n\n const verifyMagicLink = async (\n params: VerifyMagicLinkParams\n ): Promise<VerifyMagicLinkResponse> => {\n const { token, email, tenantSlug: targetSlug } = params;\n\n // RFC-001: Get tenantId from slug if provided, otherwise use current context\n let resolvedTenantId = tenant?.id;\n let targetTenantSlug = tenantSlug;\n let targetSessionManager = sessionManager;\n\n if (targetSlug) {\n // Get tenant ID from public endpoint using slug\n const tenantApi = new TenantApiService(authenticatedHttpService, appId);\n const tenantInfo = await tenantApi.getPublicTenantInfo(targetSlug);\n resolvedTenantId = tenantInfo.id;\n targetTenantSlug = targetSlug;\n }\n\n const verifyResponse = await authApiService.verifyMagicLink({\n token,\n email,\n appId,\n tenantId: resolvedTenantId,\n });\n\n // Check if we need to switch\n const shouldSwitch = targetSlug && targetSlug !== tenantSlug;\n\n // Save tokens to the correct tenant\n\n if (shouldSwitch) {\n // If switching, create a new SessionManager for the target tenant\n targetSessionManager = new SessionManager({\n tenantSlug: targetTenantSlug,\n baseUrl: baseUrl,\n });\n }\n targetSessionManager.setTokens({\n accessToken: verifyResponse.accessToken,\n refreshToken: verifyResponse.refreshToken,\n expiresIn: verifyResponse.expiresIn,\n });\n\n // Store user data\n if (verifyResponse.user) {\n targetSessionManager.setUser(verifyResponse.user);\n setCurrentUser(verifyResponse.user);\n\n // Load complete user data from API after magic link login\n try {\n await loadUserData();\n } catch (error) {\n console.warn('Failed to load complete user data after magic link login:', error);\n }\n }\n\n // Now perform the switch if needed\n if (shouldSwitch && targetTenantSlug && targetTenantSlug !== tenantSlug) {\n // Pass tokens for cross-subdomain auth\n switchTenant(targetTenantSlug, {\n tokens: {\n accessToken: verifyResponse.accessToken,\n refreshToken: verifyResponse.refreshToken,\n expiresIn: verifyResponse.expiresIn,\n },\n });\n // Code after this won't execute due to page reload\n }\n\n return verifyResponse;\n };\n\n const refreshToken = async () => {\n const tokens = sessionManager.getTokens();\n if (!tokens?.refreshToken) {\n throw new Error('No refresh token available');\n }\n\n const refreshResponse = await authApiService.refreshToken({\n refreshToken: tokens.refreshToken,\n });\n\n sessionManager.setTokens({\n accessToken: refreshResponse.accessToken,\n refreshToken: refreshResponse.refreshToken || tokens.refreshToken,\n expiresIn: refreshResponse.expiresIn,\n });\n };\n\n const logout = () => {\n sessionManager.clearSession();\n setCurrentUser(null);\n setUserError(null);\n // RFC-004: Clear multi-tenant state\n setUserTenants([]);\n setHasTenantContext(false);\n try {\n localStorage.removeItem('userTenants');\n } catch {\n // Ignore localStorage errors\n }\n };\n\n const setTokens = (tokens: {\n accessToken: string;\n refreshToken: string;\n expiresIn: number;\n }) => {\n sessionManager.setTokens(tokens);\n };\n\n const hasValidSession = () => {\n return sessionManager.hasValidSession();\n };\n\n const clearSession = () => {\n sessionManager.clearSession();\n setCurrentUser(null);\n setUserError(null);\n };\n\n // Role and Permission methods\n const fetchRoles = async () => {\n if (!appId) return;\n\n try {\n setRolesLoading(true);\n const { roles } = await roleApiService.getRolesByApp(appId);\n setAvailableRoles(roles);\n } catch (error) {\n console.error('Failed to fetch roles:', error);\n } finally {\n setRolesLoading(false);\n }\n };\n\n const refreshRoles = async () => {\n await fetchRoles();\n };\n\n // Helper functions for permission checks\n const hasPermission = (permission: string | Permission): boolean => {\n if (!userPermissions || userPermissions.length === 0) {\n return false;\n }\n\n if (typeof permission === 'string') {\n // userPermissions is now an array of strings in 'resource.action' format\n return userPermissions.includes(permission);\n }\n\n // For Permission objects, convert to string and check\n const permissionString = `${permission.resource}.${permission.action}`;\n return userPermissions.includes(permissionString);\n };\n\n const hasAnyPermission = (permissions: (string | Permission)[]): boolean => {\n return permissions.some(permission => hasPermission(permission));\n };\n\n const hasAllPermissions = (permissions: (string | Permission)[]): boolean => {\n return permissions.every(permission => hasPermission(permission));\n };\n\n // Utility function to get all user permissions in resource.action format\n const getUserPermissionStrings = (): string[] => {\n if (!userPermissions) return [];\n // userPermissions is already an array of strings\n return userPermissions;\n };\n\n // RFC-004: Switch to a different tenant without re-authentication\n const switchToTenant = async (\n tenantId: string,\n options?: { redirectPath?: string }\n ): Promise<void> => {\n const { redirectPath } = options || {};\n\n // Get refresh token from session\n const tokens = sessionManager.getTokens();\n if (!tokens?.refreshToken) {\n throw new Error('No refresh token available for tenant switch');\n }\n\n // Call switch-tenant endpoint\n const response = await authApiService.switchTenant({\n refreshToken: tokens.refreshToken,\n tenantId,\n });\n\n // Update tokens with tenant-scoped token\n sessionManager.setTokens({\n accessToken: response.accessToken,\n refreshToken: tokens.refreshToken, // Keep the same refresh token\n expiresIn: response.expiresIn,\n });\n\n // Update user with tenant context\n setCurrentUser(response.user);\n sessionManager.setUser(response.user);\n setHasTenantContext(true);\n\n // Find target tenant info from userTenants\n const targetTenant = userTenants.find(t => t.id === tenantId);\n\n if (targetTenant) {\n // Use TenantProvider's switchTenant for URL handling\n switchTenant(targetTenant.subdomain, {\n tokens: {\n accessToken: response.accessToken,\n refreshToken: tokens.refreshToken,\n expiresIn: response.expiresIn,\n },\n redirectPath,\n });\n // Code after this won't execute due to page reload\n }\n };\n\n // RFC-004: Refresh user tenants from backend\n const refreshUserTenants = async (): Promise<UserTenantMembership[]> => {\n const authHeaders = await sessionManager.getAuthHeaders();\n const tenants = await authApiService.getUserTenants(authHeaders);\n setUserTenants(tenants);\n // Cache in localStorage\n try {\n localStorage.setItem('userTenants', JSON.stringify(tenants));\n } catch {\n // Ignore localStorage errors\n }\n return tenants;\n };\n\n return {\n // RFC-003: Authentication state\n isAuthenticated,\n sessionManager,\n authenticatedHttpService,\n login,\n signup,\n signupTenantAdmin,\n sendMagicLink,\n verifyMagicLink,\n changePassword,\n requestPasswordReset,\n confirmPasswordReset,\n refreshToken,\n logout,\n setTokens,\n hasValidSession,\n clearSession,\n currentUser,\n isUserLoading,\n userError,\n loadUserData,\n refreshUser,\n isAuthInitializing: !isAuthReady,\n isAuthReady,\n userRole,\n userPermissions,\n availableRoles,\n rolesLoading,\n hasPermission,\n hasAnyPermission,\n hasAllPermissions,\n getUserPermissionStrings,\n refreshRoles,\n // RFC-004: Multi-tenant user membership\n userTenants,\n hasTenantContext,\n switchToTenant,\n refreshUserTenants,\n };\n }, [\n isAuthenticated,\n sessionManager,\n authenticatedHttpService,\n authApiService,\n userApiService,\n roleApiService,\n appId,\n tenant,\n tenantSlug,\n switchTenant,\n availableRoles,\n currentUser,\n isUserLoading,\n userError,\n userTenants,\n hasTenantContext,\n isAuthReady,\n userRole,\n userPermissions,\n ]);\n\n // Keep loadUserDataRef in sync with the latest contextValue.loadUserData\n loadUserDataRef.current = contextValue.loadUserData;\n\n // Fetch roles on mount if not provided via SSR\n useEffect(() => {\n if (!config.initialRoles && appId) {\n const fetchRoles = async () => {\n try {\n setRolesLoading(true);\n const internalHttpService = new HttpService(baseUrl);\n const roleApiService = new RoleApiService(internalHttpService);\n const { roles } = await roleApiService.getRolesByApp(appId);\n setAvailableRoles(roles);\n } catch (error) {\n console.error('Failed to fetch roles:', error);\n } finally {\n setRolesLoading(false);\n }\n };\n\n fetchRoles();\n }\n }, [appId, baseUrl, config.initialRoles]);\n\n // Cross-subdomain auth: Clean URL and load user data after sync token processing\n const [urlTokensCleanedUp, setUrlTokensCleanedUp] = useState(false);\n useEffect(() => {\n if (urlTokensCleanedUp) return;\n setUrlTokensCleanedUp(true);\n\n // Clean URL if we had tokens (tokens already saved to sessionManager synchronously)\n if (initRef.current.urlTokens) {\n console.log('[AuthProvider] EFFECT: Cleaning up URL after sync token processing');\n clearAuthTokensFromUrl();\n\n // Block auth ready until user data is loaded\n setIsLoadingAfterUrlTokens(true);\n console.log('[AuthProvider] EFFECT: Loading user data (blocking isAuthReady)...');\n\n contextValue\n .loadUserData()\n .catch(error => {\n console.error('[AuthProvider] Failed to load user data:', error);\n })\n .finally(() => {\n console.log('[AuthProvider] EFFECT: User data loaded, releasing isAuthReady');\n setIsLoadingAfterUrlTokens(false);\n });\n }\n }, [contextValue, urlTokensCleanedUp]);\n\n // Initialize user data from session on mount.\n // If a background refresh is in progress (expired token being renewed), wait for it\n // before checking session validity — prevents premature redirect to login.\n useEffect(() => {\n let cancelled = false;\n\n const init = async () => {\n // If token is expired but a refresh is pending, wait for it\n if (!sessionManager.hasValidSession() && sessionManager.getTokens()?.refreshToken) {\n await sessionManager.waitForPendingRefresh();\n }\n if (cancelled) return;\n\n const user = sessionManager.getUser();\n if (user && sessionManager.hasValidSession()) {\n setCurrentUser(user);\n }\n // Always release — valid session with no cached user will be handled\n // by the auto-load effect (triggered via contextValue change when isAuthReady flips)\n setIsRestoringSession(false);\n };\n init();\n\n return () => {\n cancelled = true;\n };\n }, [sessionManager]);\n\n // Auto-load user data if we have tokens but no currentUser\n useEffect(() => {\n // Wait until URL tokens cleanup is done before auto-loading\n if (!urlTokensCleanedUp) return;\n\n // If we had URL tokens, user data is already being loaded by the cleanup effect\n if (initRef.current.urlTokens) return;\n\n // Only trigger auto-load if we don't have currentUser and not already loading\n if (!currentUser && !isUserLoading && !userError && sessionManager.hasValidSession()) {\n console.log('[AuthProvider] Auto-loading user data...');\n loadUserDataRef\n .current()\n .catch(() => {\n // Silent fail - error already logged in loadUserData\n })\n .finally(() => {\n setIsRestoringSession(false);\n });\n } else if (currentUser) {\n setIsRestoringSession(false);\n } else {\n // No valid session or error — release\n setIsRestoringSession(false);\n }\n }, [currentUser, isUserLoading, userError, sessionManager, urlTokensCleanedUp]);\n\n return <AuthContext.Provider value={contextValue}>{children}</AuthContext.Provider>;\n}\n\nexport function useAuth(): AuthContextValue {\n const context = useContext(AuthContext);\n if (!context) {\n throw new Error('useAuth must be used within an AuthProvider');\n }\n return context;\n}\n\n/**\n * Optional hook that returns AuthContext if available, null otherwise.\n * Useful for components that may or may not be inside an AuthProvider.\n */\nexport function useAuthOptional(): AuthContextValue | null {\n return useContext(AuthContext);\n}\n","import { HttpService } from './HttpService';\nimport { SessionManager } from './SessionManager';\nimport type {\n ApiResponse,\n FeatureFlagItem,\n FeatureFlagValueResponse,\n FeatureFlag,\n CreateFeatureFlagRequest,\n PaginationParams,\n} from '../types/api';\n\nexport class FeatureFlagApiService {\n constructor(\n private httpService: HttpService,\n private sessionManager?: SessionManager\n ) {}\n\n async createFeatureFlag(request: CreateFeatureFlagRequest): Promise<FeatureFlag> {\n if (!this.sessionManager) {\n throw new Error('SessionManager is required for private endpoints');\n }\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const response = await this.httpService.post<ApiResponse<FeatureFlag>>(\n '/feature-flags/',\n request,\n {\n headers: authHeaders,\n }\n );\n return response.data;\n }\n\n async getFeatureFlags(\n params?: PaginationParams\n ): Promise<{ featureFlags: FeatureFlag[]; meta: any }> {\n if (!this.sessionManager) {\n throw new Error('SessionManager is required for private endpoints');\n }\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const queryParams = new URLSearchParams();\n\n if (params?.page) queryParams.append('page', params.page.toString());\n if (params?.limit) queryParams.append('limit', params.limit.toString());\n if (params?.sortBy) queryParams.append('sortBy', params.sortBy);\n if (params?.sortOrder) queryParams.append('sortOrder', params.sortOrder);\n\n const url = `/feature-flags/${queryParams.toString() ? `?${queryParams.toString()}` : ''}`;\n const response = await this.httpService.get<ApiResponse<FeatureFlag[]>>(url, {\n headers: authHeaders,\n });\n\n return {\n featureFlags: response.data,\n meta: response.meta,\n };\n }\n\n async getFeatureFlagById(id: string): Promise<FeatureFlag> {\n if (!this.sessionManager) {\n throw new Error('SessionManager is required for private endpoints');\n }\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const response = await this.httpService.get<ApiResponse<FeatureFlag>>(`/feature-flags/${id}`, {\n headers: authHeaders,\n });\n return response.data;\n }\n\n async updateFeatureFlag(\n id: string,\n request: Partial<CreateFeatureFlagRequest>\n ): Promise<FeatureFlag> {\n if (!this.sessionManager) {\n throw new Error('SessionManager is required for private endpoints');\n }\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const response = await this.httpService.put<ApiResponse<FeatureFlag>>(\n `/feature-flags/${id}`,\n request,\n {\n headers: authHeaders,\n }\n );\n return response.data;\n }\n\n async deleteFeatureFlag(id: string): Promise<void> {\n if (!this.sessionManager) {\n throw new Error('SessionManager is required for private endpoints');\n }\n const authHeaders = await this.sessionManager.getAuthHeaders();\n await this.httpService.delete<void>(`/feature-flags/${id}`, {\n headers: authHeaders,\n });\n }\n\n // Public endpoint - no auth required\n async getTenantFeatureFlags(tenantId: string, appId: string): Promise<FeatureFlagItem[]> {\n if (!tenantId || !appId) {\n throw new Error('Tenant ID and App ID are required');\n }\n\n const queryParams = new URLSearchParams();\n queryParams.append('tenantId', tenantId);\n queryParams.append('appId', appId);\n\n const url = `/tenant-feature-flags${queryParams.toString() ? `?${queryParams.toString()}` : ''}`;\n const response = await this.httpService.get<ApiResponse<FeatureFlagItem[]>>(url, {\n headers: { 'X-Tenant-ID': tenantId },\n });\n\n return response.data;\n }\n\n // Public endpoint - no auth required\n async getTenantFeatureFlag(\n flagKey: string,\n tenantId: string,\n appId: string\n ): Promise<FeatureFlagValueResponse> {\n if (!flagKey || !tenantId || !appId) {\n throw new Error('Flag Key, Tenant ID and App ID are required');\n }\n\n const queryParams = new URLSearchParams();\n queryParams.append('tenantId', tenantId);\n queryParams.append('appId', appId);\n\n const url = `/tenant-feature-flags/${flagKey}${queryParams.toString() ? `?${queryParams.toString()}` : ''}`;\n const response = await this.httpService.get<ApiResponse<FeatureFlagValueResponse>>(url, {\n headers: { 'X-Tenant-ID': tenantId },\n });\n\n return response.data;\n }\n}\n","import { createContext, useContext, ReactNode, useMemo, useState, useEffect } from 'react';\nimport { FeatureFlagApiService } from '../services/FeatureFlagApiService';\nimport { HttpService } from '../services/HttpService';\nimport { useAppOptional } from './AppProvider';\nimport { useTenantOptional } from './TenantProvider';\nimport type { FeatureFlagItem } from '../types/api';\n\nexport interface FeatureFlagConfig {\n refreshInterval?: number; // in milliseconds, default 5 minutes\n onError?: (error: Error) => void;\n}\n\nexport interface FeatureFlagContextValue {\n featureFlags: FeatureFlagItem[];\n loading: boolean;\n error: string | null;\n isReady: boolean;\n isEnabled: (flagName: string) => boolean;\n getFlag: (flagName: string) => FeatureFlagItem | undefined;\n refresh: () => Promise<void>;\n}\n\nconst FeatureFlagContext = createContext<FeatureFlagContextValue | null>(null);\n\ninterface FeatureFlagProviderProps {\n config?: FeatureFlagConfig;\n children: ReactNode;\n}\n\nexport function FeatureFlagProvider({ config = {}, children }: FeatureFlagProviderProps) {\n // Use optional hooks - provider works even if App/Tenant not ready yet\n const appContext = useAppOptional();\n const tenantContext = useTenantOptional();\n\n const baseUrl = appContext?.baseUrl ?? '';\n const appId = appContext?.appId ?? '';\n const tenant = tenantContext?.tenant ?? null;\n\n const [featureFlags, setFeatureFlags] = useState<FeatureFlagItem[]>([]);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [initialLoadDone, setInitialLoadDone] = useState(false);\n\n const featureFlagService = useMemo(() => {\n const httpService = new HttpService(baseUrl);\n return new FeatureFlagApiService(httpService);\n }, [baseUrl]);\n\n const fetchFeatureFlags = async () => {\n if (!tenant?.id) {\n setFeatureFlags([]);\n return;\n }\n\n setLoading(true);\n setError(null);\n\n try {\n const response = await featureFlagService.getTenantFeatureFlags(tenant.id, appId);\n setFeatureFlags(response);\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : 'Failed to fetch feature flags';\n setError(errorMessage);\n if (config.onError) {\n config.onError(err instanceof Error ? err : new Error(errorMessage));\n }\n } finally {\n setLoading(false);\n }\n };\n\n // Initial fetch and setup refresh interval\n useEffect(() => {\n // Wait for dependencies to be ready\n if (!baseUrl || !appId) return;\n\n fetchFeatureFlags().finally(() => setInitialLoadDone(true));\n\n const refreshInterval = config.refreshInterval || 5 * 60 * 1000; // 5 minutes default\n const interval = setInterval(fetchFeatureFlags, refreshInterval);\n\n return () => clearInterval(interval);\n }, [tenant?.id, baseUrl, appId, config.refreshInterval]);\n\n const contextValue = useMemo(() => {\n const isEnabled = (flagKey: string): boolean => {\n const flag = featureFlags.find(f => f.key === flagKey);\n return flag?.value === true;\n };\n\n const getFlag = (flagKey: string): FeatureFlagItem | undefined => {\n return featureFlags.find(f => f.key === flagKey);\n };\n\n const getFlagState = (flagKey: string): 'enabled' | 'disabled' | 'not_found' => {\n const flag = featureFlags.find(f => f.key === flagKey);\n if (!flag) return 'not_found';\n return flag.value ? 'enabled' : 'disabled';\n };\n\n const refresh = async () => {\n await fetchFeatureFlags();\n };\n\n // Ready when: dependencies available AND (initial load done OR no tenant needed)\n const isReady = !!(baseUrl && appId) && (initialLoadDone || !tenant?.id);\n\n return {\n featureFlags,\n loading,\n error,\n isReady,\n isEnabled,\n getFlag,\n getFlagState,\n refresh,\n };\n }, [featureFlags, loading, error, baseUrl, appId, tenant?.id, initialLoadDone]);\n\n return <FeatureFlagContext.Provider value={contextValue}>{children}</FeatureFlagContext.Provider>;\n}\n\nexport function useFeatureFlags(): FeatureFlagContextValue {\n const context = useContext(FeatureFlagContext);\n if (!context) {\n throw new Error('useFeatureFlags must be used within a FeatureFlagProvider');\n }\n return context;\n}\n\n/**\n * Optional hook that returns FeatureFlagContext if available, null otherwise.\n */\nexport function useFeatureFlagsOptional(): FeatureFlagContextValue | null {\n return useContext(FeatureFlagContext);\n}\n","import { HttpService } from './HttpService';\nimport { SessionManager } from './SessionManager';\nimport type {\n Subscription,\n CreateSubscriptionRequest,\n ApiResponse,\n TenantSubscriptionFeatures,\n} from '../types/api';\n\nexport class SubscriptionApiService {\n constructor(\n private httpService: HttpService,\n private sessionManager?: SessionManager\n ) {}\n\n async createSubscription(request: CreateSubscriptionRequest): Promise<Subscription> {\n if (!this.sessionManager) {\n throw new Error('SessionManager is required for private endpoints');\n }\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const response = await this.httpService.post<ApiResponse<Subscription>>(\n '/subscriptions/',\n request,\n {\n headers: authHeaders,\n }\n );\n return response.data;\n }\n\n async getSubscriptionById(id: string): Promise<Subscription> {\n if (!this.sessionManager) {\n throw new Error('SessionManager is required for private endpoints');\n }\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const response = await this.httpService.get<ApiResponse<Subscription>>(\n `/subscriptions/subscriptions/${id}`,\n {\n headers: authHeaders,\n }\n );\n return response.data;\n }\n\n async updateSubscription(\n id: string,\n request: Partial<CreateSubscriptionRequest>\n ): Promise<Subscription> {\n if (!this.sessionManager) {\n throw new Error('SessionManager is required for private endpoints');\n }\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const response = await this.httpService.put<ApiResponse<Subscription>>(\n `/subscriptions/${id}`,\n request,\n {\n headers: authHeaders,\n }\n );\n return response.data;\n }\n\n async changeSubscriptionPlan(subscriptionId: string, planId: string): Promise<Subscription> {\n if (!this.sessionManager) {\n throw new Error('SessionManager is required for private endpoints');\n }\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const response = await this.httpService.put<ApiResponse<Subscription>>(\n `/subscriptions/${subscriptionId}/plan`,\n { planId },\n { headers: authHeaders }\n );\n return response.data;\n }\n\n // Public endpoint - no auth required\n async getTenantSubscriptionFeatures(tenantId: string): Promise<TenantSubscriptionFeatures> {\n const response = await this.httpService.get<ApiResponse<TenantSubscriptionFeatures>>(\n `/subscriptions/tenants/${tenantId}/subscription-features`\n );\n return response.data;\n }\n\n async processPayment(subscriptionId: string, paymentData: any): Promise<any> {\n if (!this.sessionManager) {\n throw new Error('SessionManager is required for private endpoints');\n }\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const response = await this.httpService.post<ApiResponse<any>>(\n `/subscriptions/${subscriptionId}/process-payment`,\n paymentData,\n { headers: authHeaders }\n );\n return response.data;\n }\n}\n","import { createContext, useContext, ReactNode, useMemo, useState, useEffect } from 'react';\nimport { SubscriptionApiService } from '../services/SubscriptionApiService';\nimport { HttpService } from '../services/HttpService';\nimport { useAppOptional } from './AppProvider';\nimport { useTenantOptional } from './TenantProvider';\nimport type { TenantSubscriptionFeatures, PlanFeature } from '../types/api';\n\nexport interface SubscriptionConfig {\n refreshInterval?: number; // in milliseconds, default 10 minutes\n onError?: (error: Error) => void;\n}\n\nexport interface SubscriptionContextValue {\n subscription: TenantSubscriptionFeatures | null;\n features: PlanFeature[];\n loading: boolean;\n error: string | null;\n isReady: boolean;\n isFeatureEnabled: (featureKey: string) => boolean;\n getFeature: (featureKey: string) => PlanFeature | undefined;\n getFeatureValue: <T = any>(featureKey: string, defaultValue?: T) => T;\n hasAllowedPlan: (allowedPlans: string[]) => boolean;\n refresh: () => Promise<void>;\n}\n\nexport interface SubscriptionProviderProps {\n config?: SubscriptionConfig;\n children: ReactNode;\n}\n\nconst SubscriptionContext = createContext<SubscriptionContextValue | undefined>(undefined);\n\nexport function SubscriptionProvider({ config = {}, children }: SubscriptionProviderProps) {\n // Use optional hooks - provider works even if App/Tenant not ready yet\n const appContext = useAppOptional();\n const tenantContext = useTenantOptional();\n\n const baseUrl = appContext?.baseUrl ?? '';\n const tenant = tenantContext?.tenant ?? null;\n\n const [subscription, setSubscription] = useState<TenantSubscriptionFeatures | null>(null);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [initialLoadDone, setInitialLoadDone] = useState(false);\n\n // Create subscription service\n const subscriptionService = useMemo(() => {\n const httpService = new HttpService(baseUrl);\n return new SubscriptionApiService(httpService);\n }, [baseUrl]);\n\n const fetchSubscription = async () => {\n if (!tenant?.id) {\n setSubscription(null);\n return;\n }\n\n setLoading(true);\n setError(null);\n\n try {\n const response = await subscriptionService.getTenantSubscriptionFeatures(tenant.id);\n setSubscription(response);\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : 'Failed to fetch subscription';\n setError(errorMessage);\n if (config.onError) {\n config.onError(err instanceof Error ? err : new Error(errorMessage));\n }\n } finally {\n setLoading(false);\n }\n };\n\n // Fetch subscription on mount and when tenant changes\n useEffect(() => {\n // Wait for dependencies to be ready\n if (!baseUrl) return;\n\n fetchSubscription().finally(() => setInitialLoadDone(true));\n\n // Set up refresh interval if configured\n if (!config.refreshInterval) return;\n\n const refreshInterval = config.refreshInterval || 10 * 60 * 1000; // 10 minutes default\n const interval = setInterval(fetchSubscription, refreshInterval);\n\n return () => clearInterval(interval);\n }, [tenant?.id, baseUrl, config.refreshInterval]);\n\n const contextValue = useMemo(() => {\n const features = subscription?.features || [];\n\n const isFeatureEnabled = (featureKey: string): boolean => {\n const feature = features.find(f => f.key === featureKey);\n if (!feature) return false;\n\n // Handle different feature types\n if (feature.type === 'BOOLEAN' || feature.type === 'boolean') {\n return feature.value === true;\n }\n\n // For non-boolean features, consider them enabled if they have a truthy value\n return Boolean(feature.value);\n };\n\n const getFeature = (featureKey: string): PlanFeature | undefined => {\n return features.find(f => f.key === featureKey);\n };\n\n const getFeatureValue = <T = any,>(featureKey: string, defaultValue?: T): T => {\n const feature = features.find(f => f.key === featureKey);\n return feature ? feature.value : (defaultValue as T);\n };\n\n const hasAllowedPlan = (allowedPlans: string[]): boolean => {\n if (!subscription || !subscription.isActive) return false;\n\n // Check if current plan is in the allowed plans array\n return allowedPlans.includes(subscription.planId);\n };\n\n const refresh = async () => {\n await fetchSubscription();\n };\n\n // Ready when: dependencies available AND (initial load done OR no tenant needed)\n const isReady = !!baseUrl && (initialLoadDone || !tenant?.id);\n\n return {\n subscription,\n features,\n loading,\n error,\n isReady,\n isFeatureEnabled,\n getFeature,\n getFeatureValue,\n hasAllowedPlan,\n refresh,\n };\n }, [subscription, loading, error, baseUrl, tenant?.id, initialLoadDone]);\n\n return (\n <SubscriptionContext.Provider value={contextValue}>{children}</SubscriptionContext.Provider>\n );\n}\n\nexport function useSubscription(): SubscriptionContextValue {\n const context = useContext(SubscriptionContext);\n if (context === undefined) {\n throw new Error('useSubscription must be used within a SubscriptionProvider');\n }\n return context;\n}\n\n/**\n * Optional hook that returns SubscriptionContext if available, null otherwise.\n */\nexport function useSubscriptionOptional(): SubscriptionContextValue | null {\n return useContext(SubscriptionContext) ?? null;\n}\n","// Common API Response Types\nexport interface ApiResponse<T> {\n success: boolean;\n message?: string;\n data: T;\n meta?: PaginationMeta;\n}\n\nexport interface ApiError {\n success: false;\n error: {\n code: string;\n };\n message: string;\n type?: 'AUTH' | 'VALIDATION' | 'BUSINESS' | 'SYSTEM';\n}\n\nexport interface PaginationMeta {\n total: number;\n page: number;\n limit: number;\n totalPages: number;\n hasNext: boolean;\n hasPrev: boolean;\n}\n\n// User Types\nexport enum UserType {\n SUPERUSER = 'SUPERUSER',\n TENANT_ADMIN = 'TENANT_ADMIN',\n USER = 'USER',\n}\n\nexport interface User {\n id: string;\n email?: string;\n phoneNumber?: string;\n name: string;\n lastName?: string;\n isActive: boolean;\n userType: UserType;\n tenantId: string | null; // null for global token, present for tenant-scoped token\n roleId: string | null;\n role?: string | null; // User's role name in current tenant context\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface CreateUserRequest {\n email?: string;\n phoneNumber?: string;\n name: string;\n lastName?: string;\n password: string;\n tenantId: string;\n userType?: string;\n roleId?: string;\n}\n\n// Auth Types\nexport interface LoginRequest {\n username: string; // Can be email or phoneNumber\n password: string;\n appId?: string;\n tenantId?: string;\n}\n\n// Tenant membership info returned from login\nexport interface UserTenantMembership {\n id: string; // Tenant ID\n name: string; // Tenant name\n subdomain: string; // Tenant subdomain\n role: string | null; // User's role in this tenant\n}\n\nexport interface LoginResponse {\n user: User;\n accessToken: string; // Global OR tenant-scoped (depends on request)\n refreshToken: string; // Valid for switch-tenant calls\n expiresIn: number;\n tenants: UserTenantMembership[]; // User's available tenants\n}\n\n// Switch tenant types\nexport interface SwitchTenantRequest {\n refreshToken: string;\n tenantId: string;\n}\n\nexport interface SwitchTenantResponse {\n accessToken: string; // Tenant-scoped token\n expiresIn: number;\n user: User; // User WITH tenantId and role\n}\n\nexport interface SignupRequest {\n email?: string;\n phoneNumber?: string;\n name: string;\n lastName?: string;\n password: string;\n tenantId?: string;\n appId?: string;\n}\n\nexport interface ChangePasswordRequest {\n currentPassword: string;\n newPassword: string;\n}\n\nexport interface RefreshTokenRequest {\n refreshToken: string;\n}\n\nexport interface RefreshTokenResponse {\n accessToken: string;\n refreshToken: string;\n expiresIn: number;\n}\n\n// Magic Link Types\nexport interface MagicLinkRequest {\n email: string;\n tenantId: string;\n frontendUrl: string;\n name?: string; // Required for signup, optional for login\n lastName?: string;\n appId?: string;\n}\n\nexport interface MagicLinkResponse {\n message: string;\n emailSent: boolean;\n}\n\nexport interface VerifyMagicLinkRequest {\n token: string;\n email: string;\n appId: string;\n tenantId?: string;\n}\n\nexport interface VerifyMagicLinkResponse {\n user: User;\n accessToken: string;\n refreshToken: string;\n expiresIn: number;\n isNewUser: boolean; // Indicates if user was created during this process\n}\n\n// Role Types\nexport interface Role {\n id: string;\n name: string;\n description: string | null;\n appId: string;\n permissions: string[]; // API returns permissions as strings in 'resource.action' format\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface CreateRoleRequest {\n name: string;\n description?: string;\n appId: string;\n permissionIds: string[];\n}\n\nexport interface AssignRoleRequest {\n userId: string;\n}\n\n// Permission Types\nexport interface Permission {\n id: string;\n name: string;\n description: string | null;\n resource: string;\n action: string;\n appId: string | null;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface CreatePermissionRequest {\n name: string;\n description?: string;\n resource: string;\n action: string;\n appId?: string;\n}\n\n// App Types\nexport interface App {\n id: string;\n name: string;\n description: string | null;\n securityLevel: 'ADMIN' | 'USER';\n isActive: boolean;\n autoApproveTenants: boolean;\n defaultSubscriptionPlanId: string | null;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface JSONSchema {\n $schema?: string;\n $id?: string;\n title?: string;\n description?: string;\n type?: 'object' | 'array' | 'string' | 'number' | 'integer' | 'boolean' | 'null';\n properties?: { [key: string]: JSONSchema };\n items?: JSONSchema;\n required?: string[];\n enum?: unknown[];\n minimum?: number;\n maximum?: number;\n minLength?: number;\n maxLength?: number;\n pattern?: string;\n format?: string;\n default?: unknown;\n placeholder?: string;\n additionalProperties?: boolean | JSONSchema;\n}\n\nexport interface PublicAppInfo {\n id: string;\n name: string;\n description: string | null;\n settingsSchema: JSONSchema | null;\n}\n\nexport interface CreateAppRequest {\n name: string;\n description?: string;\n securityLevel?: 'ADMIN' | 'USER';\n}\n\n// Tenant Types\nexport interface Tenant {\n id: string;\n name: string;\n subdomain: string;\n isActive: boolean;\n appId: string;\n settings: Record<string, any>;\n createdAt: string;\n updatedAt: string;\n isPending?: boolean;\n}\n\nexport interface CreateTenantRequest {\n name: string;\n domain?: string;\n appId: string;\n settings?: Record<string, any>;\n}\n\nexport interface PublicTenantInfo {\n id: string;\n name: string;\n subdomain: string;\n domain?: string | null; // Legacy field\n appId: string;\n}\n\nexport interface TenantSettings {\n [key: string]: any;\n}\n\nexport interface UpdateTenantSettingsRequest {\n settings: TenantSettings;\n}\n\n// Subscription Types\nexport interface Subscription {\n id: string;\n tenantId: string;\n planId: string;\n status: 'ACTIVE' | 'INACTIVE' | 'PENDING' | 'CANCELLED';\n startDate: string;\n endDate: string | null;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface CreateSubscriptionRequest {\n tenantId: string;\n planId: string;\n startDate?: string;\n endDate?: string;\n}\n\n// Subscription Plan Types\nexport interface SubscriptionPlan {\n id: string;\n name: string;\n description: string | null;\n price: number;\n currency: string;\n billingCycle: 'MONTHLY' | 'YEARLY';\n appId: string;\n features: SubscriptionFeature[];\n isActive: boolean;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface SubscriptionFeature {\n id: string;\n name: string;\n description: string | null;\n featureType: 'BOOLEAN' | 'NUMERIC' | 'TEXT';\n value: any;\n planId: string;\n}\n\nexport interface CreateSubscriptionPlanRequest {\n name: string;\n description?: string;\n price: number;\n currency: string;\n billingCycle: 'MONTHLY' | 'YEARLY';\n appId: string;\n features: CreateSubscriptionFeatureRequest[];\n}\n\nexport interface CreateSubscriptionFeatureRequest {\n name: string;\n description?: string;\n featureType: 'BOOLEAN' | 'NUMERIC' | 'TEXT';\n value: any;\n}\n\n// Feature Flag Types\nexport interface FeatureFlag {\n id: string;\n name: string;\n description: string | null;\n isActive: boolean;\n appId: string;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface CreateFeatureFlagRequest {\n name: string;\n description?: string;\n appId: string;\n}\n\n// Feature Flag API Response Types\nexport interface FeatureFlagItem {\n featureFlagId: string;\n key: string;\n name: string;\n value: boolean;\n isOverridden: boolean;\n}\n\nexport interface FeatureFlagValueResponse {\n key: string;\n value: boolean;\n}\n\n// Subscription Feature Types\nexport interface PlanFeature {\n key: string;\n name: string;\n type: 'BOOLEAN' | 'NUMBER' | 'STRING' | 'boolean' | 'number' | 'string';\n value: any;\n description?: string;\n}\n\nexport interface TenantSubscriptionFeatures {\n tenantId: string;\n planId: string;\n planName: string;\n subscriptionStatus: string;\n features: PlanFeature[];\n isActive: boolean;\n}\n\n// Common Query Parameters\nexport interface PaginationParams {\n page?: number;\n limit?: number;\n sortBy?: string;\n sortOrder?: 'ASC' | 'DESC';\n}\n","import { ReactNode } from 'react';\nimport { UserType } from './api';\n\n/**\n * Access mode for zone requirements\n * - 'required': Must match condition (redirect if not)\n * - 'forbidden': Must NOT match condition (redirect if matches)\n * - 'optional': Can be either (no redirect)\n */\nexport type AccessMode = 'required' | 'forbidden' | 'optional';\n\n/**\n * Reason types for access denial\n */\nexport type AccessDeniedType =\n | 'no_tenant'\n | 'has_tenant'\n | 'not_authenticated'\n | 'already_authenticated'\n | 'wrong_user_type'\n | 'missing_permissions';\n\n/**\n * Detailed reason object passed to callbacks when access is denied\n */\nexport interface AccessDeniedReason {\n type: AccessDeniedType;\n required?: {\n tenant?: AccessMode;\n auth?: AccessMode;\n userType?: UserType | UserType[];\n permissions?: string[];\n };\n current?: {\n hasTenant: boolean;\n isAuthenticated: boolean;\n userType?: UserType;\n permissions?: string[];\n };\n redirectTo: string;\n}\n\n/**\n * Zone root paths for automatic redirects\n */\nexport interface ZoneRoots {\n publicGuest?: string;\n publicUser?: string;\n publicAdmin?: string;\n tenantGuest?: string;\n tenantUser?: string;\n tenantAdmin?: string;\n default?: string;\n}\n\n/**\n * Default zone roots\n */\nexport const DEFAULT_ZONE_ROOTS: Required<ZoneRoots> = {\n publicGuest: '/',\n publicUser: '/account',\n publicAdmin: '/admin',\n tenantGuest: '/login',\n tenantUser: '/dashboard',\n tenantAdmin: '/admin/dashboard',\n default: '/',\n};\n\n/**\n * Preset zone configuration\n */\nexport interface ZonePresetConfig {\n tenant?: AccessMode;\n auth?: AccessMode;\n userType?: UserType | UserType[];\n requiredPermissions?: string[];\n}\n\n/**\n * Built-in zone presets\n */\nexport interface ZonePresets {\n // Public/Landing zones\n landing: ZonePresetConfig;\n publicOnly: ZonePresetConfig;\n\n // Auth zones\n login: ZonePresetConfig;\n guest: ZonePresetConfig;\n authenticated: ZonePresetConfig;\n\n // Tenant zones\n tenant: ZonePresetConfig;\n tenantOpen: ZonePresetConfig;\n tenantAuth: ZonePresetConfig;\n\n // User type zones\n user: ZonePresetConfig;\n admin: ZonePresetConfig;\n\n // Fully open\n open: ZonePresetConfig;\n}\n\n/**\n * Default preset configurations\n */\nexport const DEFAULT_ZONE_PRESETS: ZonePresets = {\n // Public/Landing zones\n landing: { tenant: 'forbidden', auth: 'optional' },\n publicOnly: { tenant: 'forbidden', auth: 'forbidden' },\n\n // Auth zones\n login: { tenant: 'required', auth: 'forbidden' },\n guest: { auth: 'forbidden' },\n authenticated: { auth: 'required' },\n\n // Tenant zones\n tenant: { tenant: 'required' },\n tenantOpen: { tenant: 'required', auth: 'optional' },\n tenantAuth: { tenant: 'required', auth: 'required' },\n\n // User type zones\n user: { tenant: 'required', auth: 'required', userType: UserType.USER },\n admin: { tenant: 'required', auth: 'required', userType: UserType.TENANT_ADMIN },\n\n // Fully open\n open: { tenant: 'optional', auth: 'optional' },\n};\n\n/**\n * Return URL storage options\n */\nexport type ReturnToStorage = 'url' | 'session' | 'local';\n\n/**\n * Routing configuration for AuthProvider\n */\nexport interface RoutingConfig {\n zoneRoots?: ZoneRoots;\n presets?: Partial<ZonePresets>;\n loadingFallback?: ReactNode;\n accessDeniedFallback?: ReactNode;\n onAccessDenied?: (reason: AccessDeniedReason) => void;\n returnToParam?: string;\n returnToStorage?: ReturnToStorage;\n}\n\n/**\n * Props for ZoneRoute component\n */\nexport interface ZoneRouteProps {\n children: ReactNode;\n\n // Preset (shorthand for common configurations)\n preset?: keyof ZonePresets | string;\n\n // Zone requirements with access modes\n tenant?: AccessMode;\n auth?: AccessMode;\n\n // User type requirements (only applies when auth='required')\n userType?: UserType | UserType[];\n\n // Permission requirements\n requiredPermissions?: string[];\n requireAllPermissions?: boolean;\n\n // Return URL handling\n returnTo?: boolean | string;\n\n // Callbacks\n onAccessDenied?: (reason: AccessDeniedReason) => void;\n\n // Fallback states (override global config)\n redirectTo?: string;\n loadingFallback?: ReactNode;\n accessDeniedFallback?: ReactNode;\n}\n\n/**\n * Zone navigation hook return type\n */\nexport interface UseZoneNavigationReturn {\n returnToUrl: string | null;\n clearReturnTo: () => void;\n setReturnTo: (url: string) => void;\n navigateToZone: (zone: keyof ZoneRoots) => void;\n getSmartRedirect: () => string;\n}\n","import { createContext, useContext, ReactNode, useMemo } from 'react';\nimport {\n RoutingConfig,\n ZoneRoots,\n ZonePresets,\n ZonePresetConfig,\n AccessDeniedReason,\n ReturnToStorage,\n DEFAULT_ZONE_ROOTS,\n DEFAULT_ZONE_PRESETS,\n} from '../types/zoneRouting';\n\n/**\n * Context value provided by RoutingProvider\n */\nexport interface RoutingContextValue {\n zoneRoots: Required<ZoneRoots>;\n presets: ZonePresets;\n loadingFallback: ReactNode;\n accessDeniedFallback: ReactNode;\n onAccessDenied?: (reason: AccessDeniedReason) => void;\n returnToParam: string;\n returnToStorage: ReturnToStorage;\n}\n\nconst RoutingContext = createContext<RoutingContextValue | null>(null);\n\nexport interface RoutingProviderProps {\n config?: RoutingConfig;\n children: ReactNode;\n}\n\n/**\n * RoutingProvider - Global configuration for zone-based routing\n *\n * Provides default redirect paths, presets, and fallbacks that ZoneRoute\n * components will use. Can be customized per-application.\n *\n * @example\n * ```tsx\n * <RoutingProvider config={{\n * zoneRoots: {\n * tenantGuest: '/auth/login',\n * tenantUser: '/app/dashboard',\n * tenantAdmin: '/admin',\n * },\n * loadingFallback: <Spinner />,\n * onAccessDenied: (reason) => console.log('Access denied:', reason),\n * }}>\n * <App />\n * </RoutingProvider>\n * ```\n */\nexport function RoutingProvider({ config = {}, children }: RoutingProviderProps) {\n const contextValue = useMemo<RoutingContextValue>(() => {\n // Merge user config with defaults\n const zoneRoots: Required<ZoneRoots> = {\n ...DEFAULT_ZONE_ROOTS,\n ...config.zoneRoots,\n };\n\n const presets: ZonePresets = {\n ...DEFAULT_ZONE_PRESETS,\n ...config.presets,\n };\n\n return {\n zoneRoots,\n presets,\n loadingFallback: config.loadingFallback ?? null,\n accessDeniedFallback: config.accessDeniedFallback ?? null,\n onAccessDenied: config.onAccessDenied,\n returnToParam: config.returnToParam ?? 'returnTo',\n returnToStorage: config.returnToStorage ?? 'url',\n };\n }, [config]);\n\n return <RoutingContext.Provider value={contextValue}>{children}</RoutingContext.Provider>;\n}\n\n/**\n * Hook to access routing configuration\n * @throws Error if used outside RoutingProvider\n */\nexport function useRouting(): RoutingContextValue {\n const context = useContext(RoutingContext);\n if (!context) {\n throw new Error('useRouting must be used within a RoutingProvider');\n }\n return context;\n}\n\n/**\n * Optional hook that returns RoutingContext if available, defaults otherwise.\n * Useful for ZoneRoute which should work with or without RoutingProvider.\n */\nexport function useRoutingOptional(): RoutingContextValue {\n const context = useContext(RoutingContext);\n\n // Return defaults if no provider\n if (!context) {\n return {\n zoneRoots: DEFAULT_ZONE_ROOTS,\n presets: DEFAULT_ZONE_PRESETS,\n loadingFallback: null,\n accessDeniedFallback: null,\n onAccessDenied: undefined,\n returnToParam: 'returnTo',\n returnToStorage: 'url',\n };\n }\n\n return context;\n}\n\n/**\n * Get a specific preset configuration\n */\nexport function useZonePreset(\n presetName: keyof ZonePresets | string\n): ZonePresetConfig | undefined {\n const { presets } = useRoutingOptional();\n return presets[presetName as keyof ZonePresets];\n}\n","import { ReactNode } from 'react';\nimport { useAuth } from '../providers/AuthProvider';\nimport { UserType, Permission } from '../types/api';\n\nexport interface ProtectedProps {\n children: ReactNode;\n fallback?: ReactNode;\n minUserType?: UserType;\n requiredPermissions?: (string | Permission)[];\n requireAllPermissions?: boolean; // If true, user must have ALL permissions. If false, user needs ANY permission\n}\n\nconst DefaultFallback = () => (\n <div\n style={{\n display: 'flex',\n flexDirection: 'column',\n justifyContent: 'center',\n alignItems: 'center',\n padding: '20px',\n backgroundColor: '#f8f9fa',\n border: '1px solid #dee2e6',\n borderRadius: '6px',\n textAlign: 'center',\n margin: '20px 0',\n }}\n >\n <div style={{ fontSize: '2rem', marginBottom: '10px' }}>🔒</div>\n <h3 style={{ color: '#495057', marginBottom: '10px' }}>Access Required</h3>\n <p style={{ color: '#6c757d', fontSize: '14px', marginBottom: '15px' }}>\n You need to be signed in to view this content.\n </p>\n <button\n style={{\n padding: '8px 16px',\n backgroundColor: '#007bff',\n color: 'white',\n border: 'none',\n borderRadius: '4px',\n cursor: 'pointer',\n fontSize: '14px',\n }}\n onClick={() => (window.location.href = '/login')}\n >\n Sign In\n </button>\n </div>\n);\n\nconst InsufficientPermissionsFallback = ({\n userType,\n minUserType,\n missingPermissions,\n}: {\n userType?: UserType;\n minUserType?: UserType;\n missingPermissions?: string[];\n}) => (\n <div\n style={{\n display: 'flex',\n flexDirection: 'column',\n justifyContent: 'center',\n alignItems: 'center',\n padding: '20px',\n backgroundColor: '#fff3cd',\n border: '1px solid #ffeaa7',\n borderRadius: '6px',\n textAlign: 'center',\n margin: '20px 0',\n }}\n >\n <div style={{ fontSize: '2rem', marginBottom: '10px' }}>⚠️</div>\n <h3 style={{ color: '#856404', marginBottom: '10px' }}>Insufficient Permissions</h3>\n {minUserType && userType ? (\n <>\n <p style={{ color: '#856404', fontSize: '14px', marginBottom: '10px' }}>\n This content requires <strong>{minUserType}</strong> access level or higher.\n </p>\n <p style={{ color: '#6c757d', fontSize: '12px' }}>\n Your current access level: <strong>{userType}</strong>\n </p>\n </>\n ) : (\n <>\n <p style={{ color: '#856404', fontSize: '14px', marginBottom: '10px' }}>\n You don't have the required permissions to view this content.\n </p>\n {missingPermissions && missingPermissions.length > 0 && (\n <p style={{ color: '#6c757d', fontSize: '12px' }}>\n Required permissions: <strong>{missingPermissions.join(', ')}</strong>\n </p>\n )}\n </>\n )}\n </div>\n);\n\n// Helper function to check if user type meets minimum requirement\nconst hasMinimumUserType = (userType: UserType, minUserType: UserType): boolean => {\n const hierarchy = {\n [UserType.USER]: 1,\n [UserType.TENANT_ADMIN]: 2,\n [UserType.SUPERUSER]: 3,\n };\n\n return hierarchy[userType] >= hierarchy[minUserType];\n};\n\nexport function Protected({\n children,\n fallback,\n minUserType,\n requiredPermissions,\n requireAllPermissions = false,\n}: ProtectedProps) {\n const { hasValidSession, sessionManager, hasPermission, hasAnyPermission, hasAllPermissions } =\n useAuth();\n\n // Check if user has a valid session\n if (!hasValidSession()) {\n return <>{fallback || <DefaultFallback />}</>;\n }\n\n const user = sessionManager.getUser();\n\n if (!user) {\n // User session exists but no user data - show fallback\n return <>{fallback || <DefaultFallback />}</>;\n }\n\n // Check user type permissions if specified\n if (minUserType && !hasMinimumUserType(user.userType, minUserType)) {\n return <InsufficientPermissionsFallback userType={user.userType} minUserType={minUserType} />;\n }\n\n // Check specific permissions if specified\n if (requiredPermissions && requiredPermissions.length > 0) {\n const hasRequiredPermissions = requireAllPermissions\n ? hasAllPermissions(requiredPermissions)\n : hasAnyPermission(requiredPermissions);\n\n if (!hasRequiredPermissions) {\n // Get missing permissions for better error message\n const missingPermissions = requiredPermissions\n .filter(permission => !hasPermission(permission))\n .map(permission => (typeof permission === 'string' ? permission : permission.name));\n\n return <InsufficientPermissionsFallback missingPermissions={missingPermissions} />;\n }\n }\n\n // User is authenticated and has sufficient permissions\n return <>{children}</>;\n}\n","import { ReactNode, useEffect } from 'react';\nimport { Navigate, useLocation } from 'react-router-dom';\nimport { useAuth } from '../providers/AuthProvider';\nimport { UserType, Permission } from '../types/api';\n\nexport interface ProtectedRouteProps {\n children: ReactNode;\n redirectTo?: string;\n requiredUserType?: UserType; // If set, only users with this exact user type can access\n requiredPermissions?: (string | Permission)[];\n requireAllPermissions?: boolean; // If true, user must have ALL permissions. If false, user needs ANY permission\n fallback?: ReactNode;\n}\n\nconst DefaultFallback = ({ redirectPath }: { redirectPath: string }) => (\n <div\n style={{\n display: 'flex',\n flexDirection: 'column',\n justifyContent: 'center',\n alignItems: 'center',\n minHeight: '100vh',\n padding: '2rem',\n backgroundColor: '#f9fafb',\n textAlign: 'center',\n }}\n >\n <div\n style={{\n backgroundColor: '#ffffff',\n padding: '2rem',\n borderRadius: '8px',\n boxShadow: '0 2px 10px rgba(0, 0, 0, 0.1)',\n maxWidth: '400px',\n }}\n >\n <div style={{ fontSize: '3rem', marginBottom: '1rem' }}>🔒</div>\n <h2 style={{ color: '#374151', marginBottom: '1rem' }}>Access Required</h2>\n <p style={{ color: '#6b7280', marginBottom: '1.5rem' }}>\n You need to be signed in to access this page.\n </p>\n <p style={{ fontSize: '0.875rem', color: '#9ca3af' }}>Redirecting to {redirectPath}...</p>\n </div>\n </div>\n);\n\nconst InsufficientPermissionsFallback = ({\n userType,\n requiredUserType,\n missingPermissions,\n}: {\n userType?: UserType;\n requiredUserType?: UserType;\n missingPermissions?: string[];\n}) => (\n <div\n style={{\n display: 'flex',\n flexDirection: 'column',\n justifyContent: 'center',\n alignItems: 'center',\n minHeight: '100vh',\n padding: '2rem',\n backgroundColor: '#f9fafb',\n textAlign: 'center',\n }}\n >\n <div\n style={{\n backgroundColor: '#ffffff',\n padding: '2rem',\n borderRadius: '8px',\n boxShadow: '0 2px 10px rgba(0, 0, 0, 0.1)',\n maxWidth: '400px',\n }}\n >\n <div style={{ fontSize: '3rem', marginBottom: '1rem' }}>⚠️</div>\n <h2 style={{ color: '#374151', marginBottom: '1rem' }}>Insufficient Permissions</h2>\n {requiredUserType && userType ? (\n <>\n <p style={{ color: '#6b7280', marginBottom: '1rem' }}>\n This page requires <strong>{requiredUserType}</strong> access.\n </p>\n <p style={{ color: '#9ca3af', fontSize: '0.875rem' }}>\n Your current user type: <strong>{userType}</strong>\n </p>\n </>\n ) : (\n <>\n <p style={{ color: '#6b7280', marginBottom: '1rem' }}>\n You don't have the required permissions to access this page.\n </p>\n {missingPermissions && missingPermissions.length > 0 && (\n <p style={{ color: '#9ca3af', fontSize: '0.875rem' }}>\n Required permissions: <strong>{missingPermissions.join(', ')}</strong>\n </p>\n )}\n </>\n )}\n </div>\n </div>\n);\n\n// Helper function to check if user type matches the required type\nconst hasRequiredUserType = (userType: UserType, requiredUserType: UserType): boolean => {\n return userType === requiredUserType;\n};\n\n/**\n * @deprecated Use `AuthenticatedZone` or `AdminZone` from './ZoneRoute' instead.\n * ProtectedRoute will be removed in a future version.\n *\n * Migration:\n * ```tsx\n * // Before\n * <ProtectedRoute redirectTo=\"/login\"><Page /></ProtectedRoute>\n *\n * // After\n * <AuthenticatedZone redirectTo=\"/login\"><Page /></AuthenticatedZone>\n *\n * // For admin routes:\n * // Before\n * <ProtectedRoute requiredUserType=\"TENANT_ADMIN\"><Admin /></ProtectedRoute>\n *\n * // After\n * <AdminZone><Admin /></AdminZone>\n * ```\n */\nexport function ProtectedRoute({\n children,\n redirectTo = '/login',\n requiredUserType,\n requiredPermissions,\n requireAllPermissions = false,\n fallback,\n}: ProtectedRouteProps) {\n const { hasValidSession, sessionManager, hasPermission, hasAnyPermission, hasAllPermissions } =\n useAuth();\n const location = useLocation();\n\n useEffect(() => {\n if (process.env.NODE_ENV === 'development') {\n console.warn(\n '[react-identity-access] ProtectedRoute is deprecated. Use AuthenticatedZone or AdminZone from ZoneRoute instead.'\n );\n }\n }, []);\n\n // Check if user has a valid session\n if (!hasValidSession()) {\n if (fallback) {\n return <>{fallback}</>;\n }\n\n return (\n <>\n <DefaultFallback redirectPath={redirectTo} />\n <Navigate to={redirectTo} state={{ from: location.pathname }} replace />\n </>\n );\n }\n\n const user = sessionManager.getUser();\n\n if (!user) {\n // User session exists but no user data - redirect to login\n return <Navigate to={redirectTo} state={{ from: location.pathname }} replace />;\n }\n\n // Check user type if specified\n if (requiredUserType && !hasRequiredUserType(user.userType, requiredUserType)) {\n return (\n <InsufficientPermissionsFallback\n userType={user.userType}\n requiredUserType={requiredUserType}\n />\n );\n }\n\n // Check specific permissions if specified\n if (requiredPermissions && requiredPermissions.length > 0) {\n const hasRequiredPermissions = requireAllPermissions\n ? hasAllPermissions(requiredPermissions)\n : hasAnyPermission(requiredPermissions);\n\n if (!hasRequiredPermissions) {\n // Get missing permissions for better error message\n const missingPermissions = requiredPermissions\n .filter(permission => !hasPermission(permission))\n .map(permission => (typeof permission === 'string' ? permission : permission.name));\n\n return <InsufficientPermissionsFallback missingPermissions={missingPermissions} />;\n }\n }\n\n // User is authenticated and has sufficient permissions\n return <>{children}</>;\n}\n","import { ReactNode, useEffect } from 'react';\nimport { Navigate, useLocation } from 'react-router-dom';\nimport { useTenantInfo } from '../providers/TenantProvider';\n\nexport interface TenantRouteProps {\n children: ReactNode;\n redirectTo?: string;\n fallback?: ReactNode;\n}\n\nconst DefaultTenantRequiredFallback = ({ redirectPath }: { redirectPath: string }) => (\n <div\n style={{\n display: 'flex',\n flexDirection: 'column',\n justifyContent: 'center',\n alignItems: 'center',\n minHeight: '100vh',\n padding: '2rem',\n backgroundColor: '#f9fafb',\n textAlign: 'center',\n }}\n >\n <div\n style={{\n backgroundColor: '#ffffff',\n padding: '2rem',\n borderRadius: '8px',\n boxShadow: '0 2px 10px rgba(0, 0, 0, 0.1)',\n maxWidth: '400px',\n }}\n >\n <div style={{ fontSize: '3rem', marginBottom: '1rem' }}>🏢</div>\n <h2 style={{ color: '#374151', marginBottom: '1rem' }}>Tenant Required</h2>\n <p style={{ color: '#6b7280', marginBottom: '1.5rem' }}>\n This page requires a tenant context to access.\n </p>\n <p style={{ fontSize: '0.875rem', color: '#9ca3af' }}>Redirecting to {redirectPath}...</p>\n </div>\n </div>\n);\n\n/**\n * @deprecated Use `TenantZone` from './ZoneRoute' instead.\n * TenantRoute will be removed in a future version.\n *\n * Migration:\n * ```tsx\n * // Before\n * <TenantRoute redirectTo=\"/\"><Page /></TenantRoute>\n *\n * // After\n * <TenantZone redirectTo=\"/\"><Page /></TenantZone>\n * ```\n */\nexport function TenantRoute({ children, redirectTo = '/', fallback }: TenantRouteProps) {\n const { tenant, isLoading, error } = useTenantInfo();\n const location = useLocation();\n\n useEffect(() => {\n if (process.env.NODE_ENV === 'development') {\n console.warn(\n '[react-identity-access] TenantRoute is deprecated. Use TenantZone from ZoneRoute instead.'\n );\n }\n }, []);\n\n // Show loading state while tenant is being detected/loaded\n if (isLoading) {\n return null; // Let TenantProvider handle loading fallback\n }\n\n // If there's an error loading tenant, let TenantProvider handle it\n if (error) {\n return null; // Let TenantProvider handle error fallback\n }\n\n // Check if tenant is required but not present\n if (!tenant) {\n if (fallback) {\n return <>{fallback}</>;\n }\n\n return (\n <>\n <DefaultTenantRequiredFallback redirectPath={redirectTo} />\n <Navigate to={redirectTo} state={{ from: location.pathname }} replace />\n </>\n );\n }\n\n // Tenant is present, render children\n return <>{children}</>;\n}\n","import { ReactNode, useEffect } from 'react';\nimport { Navigate, useLocation } from 'react-router-dom';\nimport { useTenantInfo } from '../providers/TenantProvider';\n\nexport interface LandingRouteProps {\n children: ReactNode;\n redirectTo?: string;\n fallback?: ReactNode;\n}\n\nconst DefaultTenantDetectedFallback = ({ redirectPath }: { redirectPath: string }) => (\n <div\n style={{\n display: 'flex',\n flexDirection: 'column',\n justifyContent: 'center',\n alignItems: 'center',\n minHeight: '100vh',\n padding: '2rem',\n backgroundColor: '#f9fafb',\n textAlign: 'center',\n }}\n >\n <div\n style={{\n backgroundColor: '#ffffff',\n padding: '2rem',\n borderRadius: '8px',\n boxShadow: '0 2px 10px rgba(0, 0, 0, 0.1)',\n maxWidth: '400px',\n }}\n >\n <div style={{ fontSize: '3rem', marginBottom: '1rem' }}>🚀</div>\n <h2 style={{ color: '#374151', marginBottom: '1rem' }}>Tenant Detected</h2>\n <p style={{ color: '#6b7280', marginBottom: '1.5rem' }}>\n You are accessing a tenant-specific context. Redirecting to the appropriate page.\n </p>\n <p style={{ fontSize: '0.875rem', color: '#9ca3af' }}>Redirecting to {redirectPath}...</p>\n </div>\n </div>\n);\n\n/**\n * @deprecated Use `PublicZone` from './ZoneRoute' instead.\n * LandingRoute will be removed in a future version.\n *\n * Migration:\n * ```tsx\n * // Before\n * <LandingRoute redirectTo=\"/dashboard\"><Page /></LandingRoute>\n *\n * // After\n * <PublicZone redirectTo=\"/dashboard\"><Page /></PublicZone>\n * ```\n */\nexport function LandingRoute({ children, redirectTo = '/dashboard', fallback }: LandingRouteProps) {\n const { tenant, isLoading, error } = useTenantInfo();\n const location = useLocation();\n\n useEffect(() => {\n if (process.env.NODE_ENV === 'development') {\n console.warn(\n '[react-identity-access] LandingRoute is deprecated. Use PublicZone from ZoneRoute instead.'\n );\n }\n }, []);\n\n // Show loading state while tenant is being detected/loaded\n if (isLoading) {\n return null; // Let TenantProvider handle loading fallback\n }\n\n // If there's an error loading tenant, let TenantProvider handle it\n if (error) {\n return null; // Let TenantProvider handle error fallback\n }\n\n // Check if tenant is present (should redirect to tenant-specific route)\n if (tenant) {\n if (fallback) {\n return <>{fallback}</>;\n }\n\n return (\n <>\n <DefaultTenantDetectedFallback redirectPath={redirectTo} />\n <Navigate to={redirectTo} state={{ from: location.pathname }} replace />\n </>\n );\n }\n\n // No tenant present, render public landing page\n return <>{children}</>;\n}\n","import { FC, useEffect, useMemo } from 'react';\nimport { Navigate, useLocation } from 'react-router-dom';\nimport { useAuth } from '../providers/AuthProvider';\nimport { useTenant } from '../providers/TenantProvider';\nimport { useRoutingOptional } from '../providers/RoutingProvider';\nimport { UserType } from '../types/api';\nimport {\n AccessMode,\n AccessDeniedReason,\n AccessDeniedType,\n ZoneRouteProps,\n ZonePresetConfig,\n ZoneRoots,\n ReturnToStorage,\n} from '../types/zoneRouting';\n\ninterface ZoneState {\n hasTenant: boolean;\n isAuthenticated: boolean;\n userType?: UserType;\n permissions: string[];\n isLoading: boolean;\n}\n\n/**\n * Check if user type matches requirement\n */\nfunction matchesUserType(\n currentType: UserType | undefined,\n required: UserType | UserType[] | undefined\n): boolean {\n if (!required) return true;\n if (!currentType) return false;\n\n if (Array.isArray(required)) {\n return required.includes(currentType);\n }\n return currentType === required;\n}\n\n/**\n * Check access mode condition\n */\nfunction checkAccessMode(\n mode: AccessMode | undefined,\n condition: boolean\n): 'pass' | 'fail' | 'skip' {\n if (!mode || mode === 'optional') return 'skip';\n if (mode === 'required') return condition ? 'pass' : 'fail';\n if (mode === 'forbidden') return condition ? 'fail' : 'pass';\n return 'skip';\n}\n\n/**\n * Determine access denied type based on requirements and state\n */\nfunction getAccessDeniedType(\n requirements: {\n tenant?: AccessMode;\n auth?: AccessMode;\n userType?: UserType | UserType[];\n permissions?: string[];\n requireAllPermissions?: boolean;\n },\n state: ZoneState\n): AccessDeniedType | null {\n // Check tenant requirement\n const tenantCheck = checkAccessMode(requirements.tenant, state.hasTenant);\n if (tenantCheck === 'fail') {\n return state.hasTenant ? 'has_tenant' : 'no_tenant';\n }\n\n // Check auth requirement\n const authCheck = checkAccessMode(requirements.auth, state.isAuthenticated);\n if (authCheck === 'fail') {\n return state.isAuthenticated ? 'already_authenticated' : 'not_authenticated';\n }\n\n // Check user type (only if auth is required or optional with authenticated user)\n if (requirements.userType && state.isAuthenticated) {\n if (!matchesUserType(state.userType, requirements.userType)) {\n return 'wrong_user_type';\n }\n }\n\n // Check permissions\n if (requirements.permissions && requirements.permissions.length > 0) {\n const checkFn =\n requirements.requireAllPermissions !== false\n ? (perms: string[]) => perms.every(p => state.permissions.includes(p))\n : (perms: string[]) => perms.some(p => state.permissions.includes(p));\n if (!checkFn(requirements.permissions)) {\n return 'missing_permissions';\n }\n }\n\n return null;\n}\n\n/**\n * Get smart redirect based on current state\n */\nfunction getSmartRedirect(state: ZoneState, zoneRoots: Required<ZoneRoots>): string {\n if (!state.hasTenant) {\n if (!state.isAuthenticated) return zoneRoots.publicGuest;\n if (state.userType === UserType.TENANT_ADMIN) return zoneRoots.publicAdmin;\n return zoneRoots.publicUser;\n } else {\n if (!state.isAuthenticated) return zoneRoots.tenantGuest;\n if (state.userType === UserType.TENANT_ADMIN) return zoneRoots.tenantAdmin;\n return zoneRoots.tenantUser;\n }\n}\n\n/**\n * Build redirect URL with return path\n */\nfunction buildRedirectUrl(\n redirectTo: string,\n returnPath: string | boolean | undefined,\n currentPath: string,\n returnToParam: string,\n returnToStorage: ReturnToStorage\n): string {\n if (!returnPath || returnToStorage !== 'url') {\n return redirectTo;\n }\n\n const returnUrl = typeof returnPath === 'string' ? returnPath : currentPath;\n const separator = redirectTo.includes('?') ? '&' : '?';\n return `${redirectTo}${separator}${returnToParam}=${encodeURIComponent(returnUrl)}`;\n}\n\n/**\n * Store return URL in session/local storage\n */\nfunction storeReturnUrl(\n returnPath: string | boolean | undefined,\n currentPath: string,\n returnToStorage: ReturnToStorage\n): void {\n if (!returnPath || returnToStorage === 'url') return;\n\n const returnUrl = typeof returnPath === 'string' ? returnPath : currentPath;\n const key = 'zone_return_to';\n\n if (returnToStorage === 'session') {\n sessionStorage.setItem(key, returnUrl);\n } else if (returnToStorage === 'local') {\n localStorage.setItem(key, returnUrl);\n }\n}\n\n/**\n * ZoneRoute - Unified route protection component\n *\n * Replaces TenantRoute, LandingRoute, and ProtectedRoute with a single\n * flexible component that supports:\n * - Tenant context requirements (required/forbidden/optional)\n * - Authentication requirements (required/forbidden/optional)\n * - User type filtering\n * - Permission checks\n * - Smart redirects\n * - Return URL handling\n */\nexport const ZoneRoute: FC<ZoneRouteProps> = ({\n children,\n preset,\n tenant: tenantMode,\n auth: authMode,\n userType,\n requiredPermissions,\n requireAllPermissions = true,\n returnTo,\n onAccessDenied,\n redirectTo,\n loadingFallback,\n accessDeniedFallback,\n}) => {\n const location = useLocation();\n const { isAuthenticated, isAuthInitializing, currentUser, userPermissions } = useAuth();\n const { tenant, isTenantLoading } = useTenant();\n\n // Get global routing config (works with or without RoutingProvider)\n const routingConfig = useRoutingOptional();\n\n // Resolve preset configuration from global config\n const presetConfig: ZonePresetConfig | undefined = useMemo(() => {\n if (!preset) return undefined;\n return routingConfig.presets[preset as keyof typeof routingConfig.presets];\n }, [preset, routingConfig.presets]);\n\n // Merge preset with explicit props (explicit props take precedence)\n const requirements = useMemo(\n () => ({\n tenant: tenantMode ?? presetConfig?.tenant,\n auth: authMode ?? presetConfig?.auth,\n userType: userType ?? presetConfig?.userType,\n permissions: requiredPermissions ?? presetConfig?.requiredPermissions,\n requireAllPermissions,\n }),\n [tenantMode, authMode, userType, requiredPermissions, presetConfig, requireAllPermissions]\n );\n\n // Current state\n const state: ZoneState = useMemo(\n () => ({\n hasTenant: Boolean(tenant),\n isAuthenticated,\n userType: currentUser?.userType,\n permissions: userPermissions,\n isLoading: isAuthInitializing || isTenantLoading,\n }),\n [\n tenant,\n isAuthenticated,\n currentUser?.userType,\n userPermissions,\n isAuthInitializing,\n isTenantLoading,\n ]\n );\n\n // Check access\n const accessDeniedType = useMemo(() => {\n if (state.isLoading) return null;\n return getAccessDeniedType(requirements, state);\n }, [requirements, state]);\n\n // Calculate redirect target using global config\n const redirectTarget = useMemo(() => {\n if (!accessDeniedType) return null;\n return redirectTo || getSmartRedirect(state, routingConfig.zoneRoots);\n }, [accessDeniedType, redirectTo, state, routingConfig.zoneRoots]);\n\n // Build access denied reason\n const accessDeniedReason: AccessDeniedReason | null = useMemo(() => {\n if (!accessDeniedType || !redirectTarget) return null;\n return {\n type: accessDeniedType,\n required: {\n tenant: requirements.tenant,\n auth: requirements.auth,\n userType: requirements.userType,\n permissions: requirements.permissions,\n },\n current: {\n hasTenant: state.hasTenant,\n isAuthenticated: state.isAuthenticated,\n userType: state.userType,\n permissions: state.permissions,\n },\n redirectTo: redirectTarget,\n };\n }, [accessDeniedType, redirectTarget, requirements, state]);\n\n // Call onAccessDenied callback (local or global)\n useEffect(() => {\n if (accessDeniedReason) {\n // Local callback takes precedence\n if (onAccessDenied) {\n onAccessDenied(accessDeniedReason);\n } else if (routingConfig.onAccessDenied) {\n routingConfig.onAccessDenied(accessDeniedReason);\n }\n }\n }, [accessDeniedReason, onAccessDenied, routingConfig]);\n\n // Handle return URL storage for non-URL storage modes\n useEffect(() => {\n if (accessDeniedReason && returnTo) {\n storeReturnUrl(returnTo, location.pathname + location.search, routingConfig.returnToStorage);\n }\n }, [\n accessDeniedReason,\n returnTo,\n location.pathname,\n location.search,\n routingConfig.returnToStorage,\n ]);\n\n // Loading state (local fallback takes precedence over global)\n if (state.isLoading) {\n return <>{loadingFallback ?? routingConfig.loadingFallback ?? null}</>;\n }\n\n // Access denied - redirect\n if (accessDeniedReason && redirectTarget) {\n // Show access denied fallback briefly before redirect (local or global)\n const fallback = accessDeniedFallback ?? routingConfig.accessDeniedFallback;\n if (fallback) {\n return <>{fallback}</>;\n }\n\n // Build final redirect URL with return path if needed\n const finalRedirect = buildRedirectUrl(\n redirectTarget,\n returnTo,\n location.pathname + location.search,\n routingConfig.returnToParam,\n routingConfig.returnToStorage\n );\n\n return <Navigate to={finalRedirect} replace />;\n }\n\n // Access granted\n return <>{children}</>;\n};\n\n// Convenience components\n\nexport const TenantZone: FC<Omit<ZoneRouteProps, 'tenant'>> = props => (\n <ZoneRoute tenant=\"required\" {...props} />\n);\n\nexport const PublicZone: FC<Omit<ZoneRouteProps, 'tenant'>> = props => (\n <ZoneRoute tenant=\"forbidden\" {...props} />\n);\n\nexport const AuthenticatedZone: FC<Omit<ZoneRouteProps, 'auth'>> = props => (\n <ZoneRoute auth=\"required\" {...props} />\n);\n\nexport const GuestZone: FC<Omit<ZoneRouteProps, 'auth'>> = props => (\n <ZoneRoute auth=\"forbidden\" {...props} />\n);\n\nexport const AdminZone: FC<Omit<ZoneRouteProps, 'auth' | 'userType'>> = props => (\n <ZoneRoute auth=\"required\" userType={UserType.TENANT_ADMIN} {...props} />\n);\n\nexport const UserZone: FC<Omit<ZoneRouteProps, 'auth' | 'userType'>> = props => (\n <ZoneRoute auth=\"required\" userType={UserType.USER} {...props} />\n);\n\nexport const OpenZone: FC<Omit<ZoneRouteProps, 'tenant' | 'auth'>> = props => (\n <ZoneRoute tenant=\"optional\" auth=\"optional\" {...props} />\n);\n\nexport const TenantAuthenticatedZone: FC<Omit<ZoneRouteProps, 'tenant' | 'auth'>> = props => (\n <ZoneRoute tenant=\"required\" auth=\"required\" {...props} />\n);\n\nexport const TenantOpenZone: FC<Omit<ZoneRouteProps, 'tenant' | 'auth'>> = props => (\n <ZoneRoute tenant=\"required\" auth=\"optional\" {...props} />\n);\n\nexport const TenantGuestZone: FC<Omit<ZoneRouteProps, 'tenant' | 'auth'>> = props => (\n <ZoneRoute tenant=\"required\" auth=\"forbidden\" {...props} />\n);\n\nexport default ZoneRoute;\n","import { ReactNode } from 'react';\nimport { useSubscription } from '../providers/SubscriptionProvider';\n\nexport interface SubscriptionGuardProps {\n children: ReactNode;\n fallback?: ReactNode;\n allowedPlans?: string[];\n requiredFeature?: string;\n}\n\nconst DefaultFallback = () => (\n <div\n style={{\n padding: '2rem',\n textAlign: 'center',\n backgroundColor: '#fef2f2',\n border: '1px solid #fecaca',\n borderRadius: '8px',\n color: '#dc2626',\n }}\n >\n <h3 style={{ margin: '0 0 1rem 0' }}>🔒 Subscription Required</h3>\n <p style={{ margin: 0 }}>\n This feature requires a higher subscription plan. Please upgrade your plan to access this\n content.\n </p>\n </div>\n);\n\nexport function SubscriptionGuard({\n children,\n fallback = <DefaultFallback />,\n allowedPlans,\n requiredFeature,\n}: SubscriptionGuardProps) {\n const { subscription, hasAllowedPlan, isFeatureEnabled, loading } = useSubscription();\n\n // Show loading state\n if (loading) {\n return (\n <div\n style={{\n padding: '2rem',\n textAlign: 'center',\n color: '#6b7280',\n }}\n >\n Loading subscription...\n </div>\n );\n }\n\n // No subscription data available\n if (!subscription) {\n return <>{fallback}</>;\n }\n\n // Check if subscription is active\n if (!subscription.isActive) {\n return <>{fallback}</>;\n }\n\n // Check allowed plans requirement\n if (allowedPlans && allowedPlans.length > 0 && !hasAllowedPlan(allowedPlans)) {\n return <>{fallback}</>;\n }\n\n // Check required feature\n if (requiredFeature && !isFeatureEnabled(requiredFeature)) {\n return <>{fallback}</>;\n }\n\n // All checks passed, render children\n return <>{children}</>;\n}\n","import { ReactNode } from 'react';\nimport { useFeatureFlags } from '../providers/FeatureFlagProvider';\n\ninterface FeatureFlagProps {\n name: string;\n children: ReactNode;\n fallback?: ReactNode;\n}\n\nconst DefaultFallback = ({ flagName }: { flagName: string }) => (\n <div\n style={{\n display: 'flex',\n flexDirection: 'column',\n justifyContent: 'center',\n alignItems: 'center',\n padding: '15px',\n backgroundColor: '#f8f9fa',\n border: '1px solid #dee2e6',\n borderRadius: '6px',\n textAlign: 'center',\n fontFamily: 'system-ui, sans-serif',\n color: '#6c757d',\n }}\n >\n <div style={{ fontSize: '24px', marginBottom: '8px' }}>🚧</div>\n <div style={{ fontSize: '14px', fontWeight: '500', marginBottom: '4px' }}>\n Feature Not Available\n </div>\n <div style={{ fontSize: '12px', opacity: 0.7 }}>Feature flag \"{flagName}\" is disabled</div>\n </div>\n);\n\nexport function FeatureFlag({ name, children, fallback }: FeatureFlagProps) {\n const { isEnabled, loading } = useFeatureFlags();\n\n // Show loading state while fetching feature flags\n if (loading) {\n return (\n <div\n style={{\n display: 'flex',\n justifyContent: 'center',\n alignItems: 'center',\n padding: '10px',\n color: '#6c757d',\n fontSize: '14px',\n }}\n >\n Loading feature flags...\n </div>\n );\n }\n\n // Show children if feature flag is enabled\n if (isEnabled(name)) {\n return <>{children}</>;\n }\n\n // Show fallback if provided, otherwise show default fallback\n return <>{fallback || <DefaultFallback flagName={name} />}</>;\n}\n","import React, { useState } from 'react';\nimport { useAuth } from '../providers/AuthProvider';\nimport { useTenantInfo } from '../providers/TenantProvider';\n\nexport interface LoginFormCopy {\n title?: string;\n usernameLabel?: string;\n usernamePlaceholder?: string;\n passwordLabel?: string;\n passwordPlaceholder?: string;\n submitButton?: string;\n forgotPasswordLink?: string;\n signupLink?: string;\n signupText?: string;\n magicLinkText?: string;\n magicLinkLink?: string;\n errorMessage?: string;\n loadingText?: string;\n}\n\nexport interface LoginFormStyles {\n container?: React.CSSProperties;\n title?: React.CSSProperties;\n form?: React.CSSProperties;\n fieldGroup?: React.CSSProperties;\n label?: React.CSSProperties;\n input?: React.CSSProperties;\n inputError?: React.CSSProperties;\n inputContainer?: React.CSSProperties;\n passwordToggle?: React.CSSProperties;\n button?: React.CSSProperties;\n buttonDisabled?: React.CSSProperties;\n buttonLoading?: React.CSSProperties;\n errorText?: React.CSSProperties;\n linkContainer?: React.CSSProperties;\n link?: React.CSSProperties;\n divider?: React.CSSProperties;\n}\n\nexport interface LoginFormIcons {\n showPassword?: React.ReactNode;\n hidePassword?: React.ReactNode;\n}\n\nexport interface LoginFormProps {\n copy?: LoginFormCopy;\n styles?: LoginFormStyles;\n icons?: LoginFormIcons;\n onSuccess?: (data: any) => void;\n onError?: (error: string) => void;\n onForgotPassword?: () => void;\n onSignupClick?: () => void;\n onMagicLinkClick?: () => void;\n showForgotPassword?: boolean;\n showSignupLink?: boolean;\n showMagicLinkOption?: boolean;\n className?: string;\n}\n\n// Default SVG icons for password toggle\nconst EyeIcon = () => (\n <svg\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n style={{ flexShrink: 0 }}\n >\n <path d=\"M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z\" />\n <circle cx=\"12\" cy=\"12\" r=\"3\" />\n </svg>\n);\n\nconst EyeOffIcon = () => (\n <svg\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n style={{ flexShrink: 0 }}\n >\n <path d=\"M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24\" />\n <line x1=\"1\" y1=\"1\" x2=\"23\" y2=\"23\" />\n </svg>\n);\n\nconst defaultIcons: Required<LoginFormIcons> = {\n showPassword: <EyeIcon />,\n hidePassword: <EyeOffIcon />,\n};\n\nconst defaultCopy: Required<LoginFormCopy> = {\n title: 'Sign In',\n usernameLabel: 'Email or Phone',\n usernamePlaceholder: 'Enter your email or phone number',\n passwordLabel: 'Password',\n passwordPlaceholder: 'Enter your password',\n submitButton: 'Sign In',\n forgotPasswordLink: 'Forgot your password?',\n signupLink: 'Sign up here',\n signupText: \"Don't have an account?\",\n magicLinkText: 'Prefer passwordless?',\n magicLinkLink: 'Use Magic Link',\n errorMessage: 'Invalid credentials',\n loadingText: 'Signing in...',\n};\n\nconst defaultStyles: Required<LoginFormStyles> = {\n container: {\n maxWidth: '400px',\n width: '100%',\n margin: '0 auto',\n padding: '2rem',\n backgroundColor: '#ffffff',\n borderRadius: '8px',\n boxShadow: '0 2px 10px rgba(0, 0, 0, 0.1)',\n },\n title: {\n fontSize: '1.5rem',\n fontWeight: 'bold',\n textAlign: 'center',\n marginBottom: '1.5rem',\n color: '#333333',\n },\n form: {\n display: 'flex',\n flexDirection: 'column',\n gap: '1rem',\n },\n fieldGroup: {\n display: 'flex',\n flexDirection: 'column',\n gap: '0.5rem',\n },\n label: {\n fontSize: '0.875rem',\n fontWeight: '500',\n color: '#374151',\n },\n input: {\n padding: '0.75rem',\n border: '1px solid #d1d5db',\n borderRadius: '6px',\n fontSize: '1rem',\n transition: 'border-color 0.15s ease-in-out',\n outline: 'none',\n width: '100%',\n },\n inputError: {\n borderColor: '#ef4444',\n boxShadow: '0 0 0 3px rgba(239, 68, 68, 0.1)',\n },\n inputContainer: {\n position: 'relative',\n display: 'flex',\n alignItems: 'center',\n },\n passwordToggle: {\n position: 'absolute',\n right: '0.75rem',\n background: 'none',\n border: 'none',\n cursor: 'pointer',\n padding: '0.25rem',\n color: '#6b7280',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n width: '24px',\n height: '24px',\n borderRadius: '4px',\n transition: 'background-color 0.15s ease-in-out',\n },\n button: {\n padding: '0.75rem 1rem',\n backgroundColor: '#3b82f6',\n color: 'white',\n border: 'none',\n borderRadius: '6px',\n fontSize: '1rem',\n fontWeight: '500',\n cursor: 'pointer',\n transition: 'background-color 0.15s ease-in-out',\n marginTop: '0.5rem',\n },\n buttonDisabled: {\n backgroundColor: '#9ca3af',\n cursor: 'not-allowed',\n },\n buttonLoading: {\n backgroundColor: '#6b7280',\n },\n errorText: {\n color: '#ef4444',\n fontSize: '0.875rem',\n textAlign: 'center',\n marginTop: '0.5rem',\n },\n linkContainer: {\n textAlign: 'center',\n marginTop: '1rem',\n },\n link: {\n color: '#3b82f6',\n textDecoration: 'none',\n fontSize: '0.875rem',\n cursor: 'pointer',\n },\n divider: {\n margin: '0.5rem 0',\n color: '#6b7280',\n fontSize: '0.875rem',\n },\n};\n\nexport function LoginForm({\n copy = {},\n styles = {},\n icons = {},\n onSuccess,\n onError,\n onForgotPassword,\n onSignupClick,\n onMagicLinkClick,\n showForgotPassword = true,\n showSignupLink = true,\n showMagicLinkOption = true,\n className,\n}: LoginFormProps) {\n const [username, setUsername] = useState('');\n const [password, setPassword] = useState('');\n const [showPassword, setShowPassword] = useState(false);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState('');\n const [fieldErrors, setFieldErrors] = useState<{ username?: boolean; password?: boolean }>({});\n\n const { login } = useAuth();\n const { tenant } = useTenantInfo();\n\n const mergedCopy = { ...defaultCopy, ...copy };\n const mergedStyles = { ...defaultStyles, ...styles };\n const mergedIcons = { ...defaultIcons, ...icons };\n\n const validateForm = () => {\n const errors: { username?: boolean; password?: boolean } = {};\n\n if (!username.trim()) errors.username = true;\n if (!password.trim()) errors.password = true;\n\n setFieldErrors(errors);\n return Object.keys(errors).length === 0;\n };\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n\n if (!validateForm()) return;\n if (!tenant?.id) {\n setError('Tenant not found');\n return;\n }\n\n setLoading(true);\n setError('');\n\n try {\n const result = await login({\n username,\n password,\n // tenantId inferred from context automatically\n });\n onSuccess?.(result);\n } catch (err: any) {\n const errorMessage = err.message || mergedCopy.errorMessage;\n setError(errorMessage);\n onError?.(errorMessage);\n } finally {\n setLoading(false);\n }\n };\n\n const getInputStyle = (field: 'username' | 'password') => ({\n ...mergedStyles.input,\n ...(fieldErrors[field] ? mergedStyles.inputError : {}),\n });\n\n const getButtonStyle = () => ({\n ...mergedStyles.button,\n ...(loading ? mergedStyles.buttonLoading : {}),\n ...(!username || !password || loading ? mergedStyles.buttonDisabled : {}),\n });\n\n return (\n <div className={className} style={mergedStyles.container}>\n <h2 style={mergedStyles.title}>{mergedCopy.title}</h2>\n\n <form onSubmit={handleSubmit} style={mergedStyles.form}>\n <div style={mergedStyles.fieldGroup}>\n <label style={mergedStyles.label}>{mergedCopy.usernameLabel}</label>\n <input\n id=\"username\"\n name=\"username\"\n type=\"text\"\n value={username}\n onChange={e => {\n setUsername(e.target.value);\n if (fieldErrors.username) {\n setFieldErrors(prev => ({ ...prev, username: false }));\n }\n }}\n placeholder={mergedCopy.usernamePlaceholder}\n style={getInputStyle('username')}\n disabled={loading}\n />\n </div>\n\n <div style={mergedStyles.fieldGroup}>\n <label style={mergedStyles.label}>{mergedCopy.passwordLabel}</label>\n <div style={mergedStyles.inputContainer}>\n <input\n id=\"password\"\n name=\"password\"\n type={showPassword ? 'text' : 'password'}\n value={password}\n onChange={e => {\n setPassword(e.target.value);\n if (fieldErrors.password) {\n setFieldErrors(prev => ({ ...prev, password: false }));\n }\n }}\n placeholder={mergedCopy.passwordPlaceholder}\n style={{\n ...getInputStyle('password'),\n paddingRight: '2.5rem', // Make room for the icon\n }}\n disabled={loading}\n />\n <button\n type=\"button\"\n onClick={() => setShowPassword(!showPassword)}\n style={mergedStyles.passwordToggle}\n disabled={loading}\n aria-label={showPassword ? 'Hide password' : 'Show password'}\n >\n {showPassword ? mergedIcons.hidePassword : mergedIcons.showPassword}\n </button>\n </div>\n </div>\n\n <button type=\"submit\" disabled={!username || !password || loading} style={getButtonStyle()}>\n {loading ? mergedCopy.loadingText : mergedCopy.submitButton}\n </button>\n\n {error && <div style={mergedStyles.errorText}>{error}</div>}\n </form>\n\n {(showForgotPassword || showSignupLink || showMagicLinkOption) && (\n <div style={mergedStyles.linkContainer}>\n {showMagicLinkOption && (\n <div>\n <span style={mergedStyles.divider}>{mergedCopy.magicLinkText} </span>\n <a onClick={onMagicLinkClick} style={mergedStyles.link}>\n {mergedCopy.magicLinkLink}\n </a>\n </div>\n )}\n\n {showMagicLinkOption && (showForgotPassword || showSignupLink) && (\n <div style={mergedStyles.divider}>•</div>\n )}\n\n {showForgotPassword && (\n <a onClick={onForgotPassword} style={mergedStyles.link}>\n {mergedCopy.forgotPasswordLink}\n </a>\n )}\n\n {showForgotPassword && showSignupLink && <div style={mergedStyles.divider}>•</div>}\n\n {showSignupLink && (\n <div>\n <span style={mergedStyles.divider}>{mergedCopy.signupText} </span>\n <a onClick={onSignupClick} style={mergedStyles.link}>\n {mergedCopy.signupLink}\n </a>\n </div>\n )}\n </div>\n )}\n </div>\n );\n}\n","import React, { useState } from 'react';\nimport { useAuth } from '../providers/AuthProvider';\nimport { useTenantInfo } from '../providers/TenantProvider';\n\nexport interface SignupFormCopy {\n title?: string;\n nameLabel?: string;\n namePlaceholder?: string;\n lastNameLabel?: string;\n lastNamePlaceholder?: string;\n emailLabel?: string;\n emailPlaceholder?: string;\n phoneNumberLabel?: string;\n phoneNumberPlaceholder?: string;\n passwordLabel?: string;\n passwordPlaceholder?: string;\n confirmPasswordLabel?: string;\n confirmPasswordPlaceholder?: string;\n tenantNameLabel?: string;\n tenantNamePlaceholder?: string;\n submitButton?: string;\n loginLink?: string;\n loginText?: string;\n magicLinkText?: string;\n magicLinkLink?: string;\n errorMessage?: string;\n loadingText?: string;\n passwordMismatchError?: string;\n isAdminLabel?: string;\n isAdminDescription?: string;\n}\n\nexport interface SignupFormStyles {\n container?: React.CSSProperties;\n title?: React.CSSProperties;\n form?: React.CSSProperties;\n fieldGroup?: React.CSSProperties;\n label?: React.CSSProperties;\n input?: React.CSSProperties;\n inputError?: React.CSSProperties;\n checkbox?: React.CSSProperties;\n checkboxContainer?: React.CSSProperties;\n checkboxLabel?: React.CSSProperties;\n button?: React.CSSProperties;\n buttonDisabled?: React.CSSProperties;\n buttonLoading?: React.CSSProperties;\n errorText?: React.CSSProperties;\n linkContainer?: React.CSSProperties;\n link?: React.CSSProperties;\n divider?: React.CSSProperties;\n}\n\nexport type SignupType = 'user' | 'tenant';\n\nexport interface SignupFormProps {\n copy?: SignupFormCopy;\n styles?: SignupFormStyles;\n signupType?: SignupType;\n onSuccess?: (data: any) => void;\n onError?: (error: string) => void;\n onLoginClick?: () => void;\n onMagicLinkClick?: () => void;\n showLoginLink?: boolean;\n showMagicLinkOption?: boolean;\n className?: string;\n}\n\nconst defaultCopy: Required<SignupFormCopy> = {\n title: 'Create Account',\n nameLabel: 'First Name',\n namePlaceholder: 'Enter your first name',\n lastNameLabel: 'Last Name',\n lastNamePlaceholder: 'Enter your last name',\n emailLabel: 'Email',\n emailPlaceholder: 'Enter your email',\n phoneNumberLabel: 'Phone Number',\n phoneNumberPlaceholder: 'Enter your phone number',\n passwordLabel: 'Password',\n passwordPlaceholder: 'Enter your password',\n confirmPasswordLabel: 'Confirm Password',\n confirmPasswordPlaceholder: 'Confirm your password',\n tenantNameLabel: 'Organization Name',\n tenantNamePlaceholder: 'Enter your organization name',\n submitButton: 'Create Account',\n loginLink: 'Sign in here',\n loginText: 'Already have an account?',\n magicLinkText: 'Prefer passwordless?',\n magicLinkLink: 'Use Magic Link',\n errorMessage: 'Failed to create account',\n loadingText: 'Creating account...',\n passwordMismatchError: 'Passwords do not match',\n isAdminLabel: 'Create new organization',\n isAdminDescription: 'Check this if you want to create a new organization',\n};\n\nconst defaultStyles: Required<SignupFormStyles> = {\n container: {\n maxWidth: '400px',\n width: '100%',\n margin: '0 auto',\n padding: '2rem',\n backgroundColor: '#ffffff',\n borderRadius: '8px',\n boxShadow: '0 2px 10px rgba(0, 0, 0, 0.1)',\n },\n title: {\n fontSize: '1.5rem',\n fontWeight: 'bold',\n textAlign: 'center',\n marginBottom: '1.5rem',\n color: '#333333',\n },\n form: {\n display: 'flex',\n flexDirection: 'column',\n gap: '1rem',\n },\n fieldGroup: {\n display: 'flex',\n flexDirection: 'column',\n gap: '0.5rem',\n },\n label: {\n fontSize: '0.875rem',\n fontWeight: '500',\n color: '#374151',\n },\n input: {\n padding: '0.75rem',\n border: '1px solid #d1d5db',\n borderRadius: '6px',\n fontSize: '1rem',\n transition: 'border-color 0.15s ease-in-out',\n outline: 'none',\n },\n inputError: {\n borderColor: '#ef4444',\n boxShadow: '0 0 0 3px rgba(239, 68, 68, 0.1)',\n },\n checkbox: {\n marginRight: '0.5rem',\n },\n checkboxContainer: {\n display: 'flex',\n alignItems: 'flex-start',\n gap: '0.5rem',\n padding: '0.5rem 0',\n },\n checkboxLabel: {\n fontSize: '0.875rem',\n color: '#374151',\n lineHeight: '1.4',\n },\n button: {\n padding: '0.75rem 1rem',\n backgroundColor: '#10b981',\n color: 'white',\n border: 'none',\n borderRadius: '6px',\n fontSize: '1rem',\n fontWeight: '500',\n cursor: 'pointer',\n transition: 'background-color 0.15s ease-in-out',\n marginTop: '0.5rem',\n },\n buttonDisabled: {\n backgroundColor: '#9ca3af',\n cursor: 'not-allowed',\n },\n buttonLoading: {\n backgroundColor: '#6b7280',\n },\n errorText: {\n color: '#ef4444',\n fontSize: '0.875rem',\n textAlign: 'center',\n marginTop: '0.5rem',\n },\n linkContainer: {\n textAlign: 'center',\n marginTop: '1rem',\n },\n link: {\n color: '#3b82f6',\n textDecoration: 'none',\n fontSize: '0.875rem',\n cursor: 'pointer',\n },\n divider: {\n margin: '0.5rem 0',\n color: '#6b7280',\n fontSize: '0.875rem',\n },\n};\n\nexport function SignupForm({\n copy = {},\n styles = {},\n signupType = 'user',\n onSuccess,\n onError,\n onLoginClick,\n onMagicLinkClick,\n showLoginLink = true,\n showMagicLinkOption = true,\n className,\n}: SignupFormProps) {\n const [name, setName] = useState('');\n const [lastName, setLastName] = useState('');\n const [email, setEmail] = useState('');\n const [phoneNumber, setPhoneNumber] = useState('');\n const [password, setPassword] = useState('');\n const [confirmPassword, setConfirmPassword] = useState('');\n const [tenantName, setTenantName] = useState('');\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState('');\n const [fieldErrors, setFieldErrors] = useState<{\n name?: boolean;\n email?: boolean;\n phoneNumber?: boolean;\n password?: boolean;\n confirmPassword?: boolean;\n tenantName?: boolean;\n }>({});\n\n const { signup, signupTenantAdmin } = useAuth();\n const { tenant } = useTenantInfo();\n\n const mergedCopy = { ...defaultCopy, ...copy };\n const mergedStyles = { ...defaultStyles, ...styles };\n\n const validateForm = () => {\n const errors: {\n name?: boolean;\n email?: boolean;\n phoneNumber?: boolean;\n password?: boolean;\n confirmPassword?: boolean;\n tenantName?: boolean;\n } = {};\n\n if (!name.trim()) errors.name = true;\n if (!email.trim() && !phoneNumber.trim()) {\n errors.email = true;\n errors.phoneNumber = true;\n }\n if (!password.trim()) errors.password = true;\n if (!confirmPassword.trim()) errors.confirmPassword = true;\n if (signupType === 'tenant' && !tenantName.trim()) errors.tenantName = true;\n\n setFieldErrors(errors);\n return Object.keys(errors).length === 0;\n };\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n\n if (!validateForm()) return;\n\n if (password !== confirmPassword) {\n setError(mergedCopy.passwordMismatchError);\n setFieldErrors({ confirmPassword: true });\n return;\n }\n\n if (signupType === 'user' && !tenant?.id) {\n setError('Tenant not found');\n return;\n }\n\n setLoading(true);\n setError('');\n\n try {\n let result;\n if (signupType === 'tenant') {\n result = await signupTenantAdmin({\n email: email || undefined,\n phoneNumber: phoneNumber || undefined,\n name,\n password,\n tenantName,\n lastName: lastName || undefined,\n });\n } else {\n result = await signup({\n email: email || undefined,\n phoneNumber: phoneNumber || undefined,\n name,\n password,\n tenantId: tenant!.id,\n lastName: lastName || undefined,\n });\n }\n onSuccess?.(result);\n } catch (err: any) {\n const errorMessage = err.message || mergedCopy.errorMessage;\n setError(errorMessage);\n onError?.(errorMessage);\n } finally {\n setLoading(false);\n }\n };\n\n const getInputStyle = (field: keyof typeof fieldErrors) => ({\n ...mergedStyles.input,\n ...(fieldErrors[field] ? mergedStyles.inputError : {}),\n });\n\n const getButtonStyle = () => ({\n ...mergedStyles.button,\n ...(loading ? mergedStyles.buttonLoading : {}),\n ...(!name ||\n (!email && !phoneNumber) ||\n !password ||\n !confirmPassword ||\n loading ||\n (signupType === 'tenant' && !tenantName)\n ? mergedStyles.buttonDisabled\n : {}),\n });\n\n const isFormValid =\n name &&\n (email || phoneNumber) &&\n password &&\n confirmPassword &&\n (signupType === 'user' || tenantName);\n\n return (\n <div className={className} style={mergedStyles.container}>\n <h2 style={mergedStyles.title}>{mergedCopy.title}</h2>\n\n <form onSubmit={handleSubmit} style={mergedStyles.form}>\n <div style={mergedStyles.fieldGroup}>\n <label style={mergedStyles.label}>{mergedCopy.nameLabel}</label>\n <input\n id=\"name\"\n name=\"name\"\n type=\"text\"\n value={name}\n onChange={e => {\n setName(e.target.value);\n if (fieldErrors.name) {\n setFieldErrors(prev => ({ ...prev, name: false }));\n }\n }}\n placeholder={mergedCopy.namePlaceholder}\n style={getInputStyle('name')}\n disabled={loading}\n />\n </div>\n\n <div style={mergedStyles.fieldGroup}>\n <label style={mergedStyles.label}>{mergedCopy.lastNameLabel}</label>\n <input\n id=\"lastName\"\n name=\"lastName\"\n type=\"text\"\n value={lastName}\n onChange={e => setLastName(e.target.value)}\n placeholder={mergedCopy.lastNamePlaceholder}\n style={mergedStyles.input}\n disabled={loading}\n />\n </div>\n\n <div style={mergedStyles.fieldGroup}>\n <label style={mergedStyles.label}>{mergedCopy.emailLabel}</label>\n <input\n id=\"email\"\n name=\"email\"\n type=\"email\"\n value={email}\n onChange={e => {\n setEmail(e.target.value);\n if (fieldErrors.email) {\n setFieldErrors(prev => ({ ...prev, email: false, phoneNumber: false }));\n }\n }}\n placeholder={mergedCopy.emailPlaceholder}\n style={getInputStyle('email')}\n disabled={loading}\n />\n </div>\n\n <div style={mergedStyles.fieldGroup}>\n <label style={mergedStyles.label}>{mergedCopy.phoneNumberLabel}</label>\n <input\n id=\"phoneNumber\"\n name=\"phoneNumber\"\n type=\"tel\"\n value={phoneNumber}\n onChange={e => {\n setPhoneNumber(e.target.value);\n if (fieldErrors.phoneNumber) {\n setFieldErrors(prev => ({ ...prev, email: false, phoneNumber: false }));\n }\n }}\n placeholder={mergedCopy.phoneNumberPlaceholder}\n style={getInputStyle('phoneNumber')}\n disabled={loading}\n />\n </div>\n\n <div\n style={{\n fontSize: '0.875rem',\n color: '#6b7280',\n textAlign: 'center',\n margin: '0.5rem 0',\n }}\n >\n At least one contact method (email or phone) is required\n </div>\n\n <div style={mergedStyles.fieldGroup}>\n <label style={mergedStyles.label}>{mergedCopy.passwordLabel}</label>\n <input\n id=\"password\"\n name=\"password\"\n type=\"password\"\n value={password}\n onChange={e => {\n setPassword(e.target.value);\n if (fieldErrors.password) {\n setFieldErrors(prev => ({ ...prev, password: false }));\n }\n }}\n placeholder={mergedCopy.passwordPlaceholder}\n style={getInputStyle('password')}\n disabled={loading}\n />\n </div>\n\n <div style={mergedStyles.fieldGroup}>\n <label style={mergedStyles.label}>{mergedCopy.confirmPasswordLabel}</label>\n <input\n id=\"confirmPassword\"\n name=\"confirmPassword\"\n type=\"password\"\n value={confirmPassword}\n onChange={e => {\n setConfirmPassword(e.target.value);\n if (fieldErrors.confirmPassword) {\n setFieldErrors(prev => ({ ...prev, confirmPassword: false }));\n }\n if (error === mergedCopy.passwordMismatchError) {\n setError('');\n }\n }}\n placeholder={mergedCopy.confirmPasswordPlaceholder}\n style={getInputStyle('confirmPassword')}\n disabled={loading}\n />\n </div>\n\n {signupType === 'tenant' && (\n <div style={mergedStyles.fieldGroup}>\n <label style={mergedStyles.label}>{mergedCopy.tenantNameLabel}</label>\n <input\n id=\"tenantName\"\n name=\"tenantName\"\n type=\"text\"\n value={tenantName}\n onChange={e => {\n setTenantName(e.target.value);\n if (fieldErrors.tenantName) {\n setFieldErrors(prev => ({ ...prev, tenantName: false }));\n }\n }}\n placeholder={mergedCopy.tenantNamePlaceholder}\n style={getInputStyle('tenantName')}\n disabled={loading}\n />\n </div>\n )}\n\n <button type=\"submit\" disabled={!isFormValid || loading} style={getButtonStyle()}>\n {loading ? mergedCopy.loadingText : mergedCopy.submitButton}\n </button>\n\n {error && <div style={mergedStyles.errorText}>{error}</div>}\n </form>\n\n {(showLoginLink || showMagicLinkOption) && (\n <div style={mergedStyles.linkContainer}>\n {showMagicLinkOption && (\n <div>\n <span style={mergedStyles.divider}>{mergedCopy.magicLinkText} </span>\n <a onClick={onMagicLinkClick} style={mergedStyles.link}>\n {mergedCopy.magicLinkLink}\n </a>\n </div>\n )}\n\n {showMagicLinkOption && showLoginLink && <div style={mergedStyles.divider}>•</div>}\n\n {showLoginLink && (\n <div>\n <span style={mergedStyles.divider}>{mergedCopy.loginText} </span>\n <a onClick={onLoginClick} style={mergedStyles.link}>\n {mergedCopy.loginLink}\n </a>\n </div>\n )}\n </div>\n )}\n </div>\n );\n}\n","import React, { useState, useEffect } from 'react';\nimport { useAuth } from '../providers/AuthProvider';\nimport { useTenantInfo } from '../providers/TenantProvider';\n\nexport interface MagicLinkFormCopy {\n title?: string;\n emailLabel?: string;\n emailPlaceholder?: string;\n nameLabel?: string;\n namePlaceholder?: string;\n lastNameLabel?: string;\n lastNamePlaceholder?: string;\n submitButton?: string;\n loginLink?: string;\n signupLink?: string;\n loginText?: string;\n signupText?: string;\n successMessage?: string;\n errorMessage?: string;\n loadingText?: string;\n verifyingText?: string;\n description?: string;\n}\n\nexport interface MagicLinkFormStyles {\n container?: React.CSSProperties;\n title?: React.CSSProperties;\n description?: React.CSSProperties;\n form?: React.CSSProperties;\n fieldGroup?: React.CSSProperties;\n label?: React.CSSProperties;\n input?: React.CSSProperties;\n inputError?: React.CSSProperties;\n button?: React.CSSProperties;\n buttonDisabled?: React.CSSProperties;\n buttonLoading?: React.CSSProperties;\n errorText?: React.CSSProperties;\n successText?: React.CSSProperties;\n linkContainer?: React.CSSProperties;\n link?: React.CSSProperties;\n divider?: React.CSSProperties;\n}\n\nexport interface MagicLinkFormProps {\n copy?: MagicLinkFormCopy;\n styles?: MagicLinkFormStyles;\n onSuccess?: (data: any) => void;\n onError?: (error: string) => void;\n onLoginClick?: () => void;\n onSignupClick?: () => void;\n showTraditionalLinks?: boolean;\n className?: string;\n // Auto-verify magic link if token is provided (e.g., from URL params)\n verifyToken?: string;\n // Frontend URL for magic link callback (if not provided, will use window.location.origin)\n frontendUrl?: string;\n}\n\nconst defaultCopy: Required<MagicLinkFormCopy> = {\n title: 'Sign In with Magic Link',\n emailLabel: 'Email',\n emailPlaceholder: 'Enter your email',\n nameLabel: 'Name',\n namePlaceholder: 'Enter your name',\n lastNameLabel: 'Last Name',\n lastNamePlaceholder: 'Enter your last name',\n submitButton: 'Send Magic Link',\n loginLink: 'Sign in with password',\n signupLink: 'Sign up with password',\n loginText: 'Already have an account?',\n signupText: 'Prefer traditional signup?',\n successMessage: 'Magic link sent! Check your email and click the link to sign in.',\n errorMessage: 'Failed to send magic link. Please try again.',\n loadingText: 'Sending magic link...',\n verifyingText: 'Verifying magic link...',\n description:\n \"Enter your email to receive a magic link. If you don't have an account, we'll create one for you.\",\n};\n\nconst defaultStyles: Required<MagicLinkFormStyles> = {\n container: {\n maxWidth: '400px',\n width: '100%',\n margin: '0 auto',\n padding: '2rem',\n backgroundColor: '#ffffff',\n borderRadius: '8px',\n boxShadow: '0 2px 10px rgba(0, 0, 0, 0.1)',\n },\n title: {\n fontSize: '1.5rem',\n fontWeight: 'bold',\n textAlign: 'center',\n marginBottom: '1rem',\n color: '#333333',\n },\n description: {\n fontSize: '0.875rem',\n color: '#6b7280',\n textAlign: 'center',\n marginBottom: '1.5rem',\n lineHeight: '1.5',\n },\n form: {\n display: 'flex',\n flexDirection: 'column',\n gap: '1rem',\n },\n fieldGroup: {\n display: 'flex',\n flexDirection: 'column',\n gap: '0.5rem',\n },\n label: {\n fontSize: '0.875rem',\n fontWeight: '500',\n color: '#374151',\n },\n input: {\n padding: '0.75rem',\n border: '1px solid #d1d5db',\n borderRadius: '6px',\n fontSize: '1rem',\n transition: 'border-color 0.15s ease-in-out',\n outline: 'none',\n width: '100%',\n },\n inputError: {\n borderColor: '#ef4444',\n boxShadow: '0 0 0 3px rgba(239, 68, 68, 0.1)',\n },\n button: {\n padding: '0.75rem 1rem',\n backgroundColor: '#3b82f6',\n color: 'white',\n border: 'none',\n borderRadius: '6px',\n fontSize: '1rem',\n fontWeight: '500',\n cursor: 'pointer',\n transition: 'background-color 0.15s ease-in-out',\n marginTop: '0.5rem',\n },\n buttonDisabled: {\n backgroundColor: '#9ca3af',\n cursor: 'not-allowed',\n },\n buttonLoading: {\n backgroundColor: '#6b7280',\n },\n errorText: {\n color: '#ef4444',\n fontSize: '0.875rem',\n textAlign: 'center',\n marginTop: '0.5rem',\n },\n successText: {\n color: '#10b981',\n fontSize: '0.875rem',\n textAlign: 'center',\n marginTop: '0.5rem',\n padding: '0.75rem',\n backgroundColor: '#f0fdf4',\n borderRadius: '6px',\n border: '1px solid #bbf7d0',\n },\n linkContainer: {\n textAlign: 'center',\n marginTop: '1rem',\n },\n link: {\n color: '#3b82f6',\n textDecoration: 'none',\n fontSize: '0.875rem',\n cursor: 'pointer',\n },\n divider: {\n margin: '0.5rem 0',\n color: '#6b7280',\n fontSize: '0.875rem',\n },\n};\n\nexport function MagicLinkForm({\n copy = {},\n styles = {},\n onSuccess,\n onError,\n onLoginClick,\n onSignupClick,\n showTraditionalLinks = true,\n className,\n verifyToken,\n frontendUrl,\n}: MagicLinkFormProps) {\n const [email, setEmail] = useState('');\n const [name, setName] = useState('');\n const [lastName, setLastName] = useState('');\n const [loading, setLoading] = useState(false);\n const [verifying, setVerifying] = useState(false);\n const [error, setError] = useState('');\n const [success, setSuccess] = useState('');\n const [fieldErrors, setFieldErrors] = useState<{ email?: boolean; name?: boolean }>({});\n const [showNameFields, setShowNameFields] = useState(false);\n\n const { sendMagicLink, verifyMagicLink } = useAuth();\n const { tenant } = useTenantInfo();\n\n const mergedCopy = { ...defaultCopy, ...copy };\n const mergedStyles = { ...defaultStyles, ...styles };\n\n // Auto-verify magic link if token is provided\n useEffect(() => {\n if (verifyToken) {\n handleVerifyMagicLink(verifyToken);\n }\n }, [verifyToken]);\n\n const handleVerifyMagicLink = async (token: string) => {\n if (!tenant || !email) {\n setError('Missing tenant or email');\n return;\n }\n\n setVerifying(true);\n setError('');\n\n try {\n const result = await verifyMagicLink({\n token,\n email,\n // tenantId inferred from context automatically\n });\n onSuccess?.(result);\n } catch (err: any) {\n const errorMessage = err.message || 'Failed to verify magic link';\n setError(errorMessage);\n onError?.(errorMessage);\n } finally {\n setVerifying(false);\n }\n };\n\n const validateForm = () => {\n const errors: { email?: boolean; name?: boolean } = {};\n\n if (!email.trim()) errors.email = true;\n if (showNameFields && !name.trim()) errors.name = true;\n\n setFieldErrors(errors);\n return Object.keys(errors).length === 0;\n };\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n\n if (!validateForm()) return;\n if (!tenant?.id) {\n setError('Tenant not found');\n return;\n }\n\n setLoading(true);\n setError('');\n setSuccess('');\n\n try {\n const finalFrontendUrl =\n frontendUrl || (typeof window !== 'undefined' ? window.location.origin : '');\n const result = await sendMagicLink({\n email,\n tenantId: tenant.id,\n frontendUrl: finalFrontendUrl,\n name: showNameFields ? name : undefined,\n lastName: showNameFields ? lastName : undefined,\n });\n setSuccess(mergedCopy.successMessage);\n onSuccess?.(result);\n } catch (err: any) {\n const errorMessage = err.message || mergedCopy.errorMessage;\n setError(errorMessage);\n onError?.(errorMessage);\n } finally {\n setLoading(false);\n }\n };\n\n const getInputStyle = (field: 'email' | 'name') => ({\n ...mergedStyles.input,\n ...(fieldErrors[field] ? mergedStyles.inputError : {}),\n });\n\n const getButtonStyle = () => ({\n ...mergedStyles.button,\n ...(loading || verifying ? mergedStyles.buttonLoading : {}),\n ...(!email || loading || verifying ? mergedStyles.buttonDisabled : {}),\n });\n\n if (verifying) {\n return (\n <div className={className} style={mergedStyles.container}>\n <h2 style={mergedStyles.title}>{mergedCopy.verifyingText}</h2>\n <div style={{ textAlign: 'center', padding: '2rem' }}>\n <div style={{ fontSize: '1rem', color: '#6b7280' }}>\n Please wait while we verify your magic link...\n </div>\n </div>\n </div>\n );\n }\n\n return (\n <div className={className} style={mergedStyles.container}>\n <h2 style={mergedStyles.title}>{mergedCopy.title}</h2>\n <p style={mergedStyles.description}>{mergedCopy.description}</p>\n\n <form onSubmit={handleSubmit} style={mergedStyles.form}>\n <div style={mergedStyles.fieldGroup}>\n <label style={mergedStyles.label}>{mergedCopy.emailLabel}</label>\n <input\n id=\"email\"\n name=\"email\"\n type=\"email\"\n value={email}\n onChange={e => {\n setEmail(e.target.value);\n if (fieldErrors.email) {\n setFieldErrors(prev => ({ ...prev, email: false }));\n }\n }}\n placeholder={mergedCopy.emailPlaceholder}\n style={getInputStyle('email')}\n disabled={loading || verifying}\n />\n </div>\n\n {/* Toggle to show name fields for new users */}\n {!showNameFields && (\n <div style={{ textAlign: 'center', marginTop: '0.5rem' }}>\n <button\n type=\"button\"\n onClick={() => setShowNameFields(true)}\n style={{\n background: 'none',\n border: 'none',\n color: '#3b82f6',\n fontSize: '0.875rem',\n cursor: 'pointer',\n textDecoration: 'underline',\n }}\n >\n New user? Add your name\n </button>\n </div>\n )}\n\n {showNameFields && (\n <>\n <div style={mergedStyles.fieldGroup}>\n <label style={mergedStyles.label}>{mergedCopy.nameLabel}</label>\n <input\n id=\"name\"\n name=\"name\"\n type=\"text\"\n value={name}\n onChange={e => {\n setName(e.target.value);\n if (fieldErrors.name) {\n setFieldErrors(prev => ({ ...prev, name: false }));\n }\n }}\n placeholder={mergedCopy.namePlaceholder}\n style={getInputStyle('name')}\n disabled={loading || verifying}\n />\n </div>\n\n <div style={mergedStyles.fieldGroup}>\n <label style={mergedStyles.label}>{mergedCopy.lastNameLabel}</label>\n <input\n id=\"lastName\"\n name=\"lastName\"\n type=\"text\"\n value={lastName}\n onChange={e => setLastName(e.target.value)}\n placeholder={mergedCopy.lastNamePlaceholder}\n style={mergedStyles.input}\n disabled={loading || verifying}\n />\n </div>\n\n <div style={{ textAlign: 'center', marginTop: '0.5rem' }}>\n <button\n type=\"button\"\n onClick={() => {\n setShowNameFields(false);\n setName('');\n setLastName('');\n }}\n style={{\n background: 'none',\n border: 'none',\n color: '#6b7280',\n fontSize: '0.875rem',\n cursor: 'pointer',\n textDecoration: 'underline',\n }}\n >\n Existing user? Hide name fields\n </button>\n </div>\n </>\n )}\n\n <button type=\"submit\" disabled={!email || loading || verifying} style={getButtonStyle()}>\n {loading ? mergedCopy.loadingText : mergedCopy.submitButton}\n </button>\n\n {error && <div style={mergedStyles.errorText}>{error}</div>}\n {success && <div style={mergedStyles.successText}>{success}</div>}\n </form>\n\n {showTraditionalLinks && (\n <div style={mergedStyles.linkContainer}>\n <div>\n <span style={mergedStyles.divider}>{mergedCopy.loginText} </span>\n <a onClick={onLoginClick} style={mergedStyles.link}>\n {mergedCopy.loginLink}\n </a>\n </div>\n\n <div style={mergedStyles.divider}>•</div>\n\n <div>\n <span style={mergedStyles.divider}>{mergedCopy.signupText} </span>\n <a onClick={onSignupClick} style={mergedStyles.link}>\n {mergedCopy.signupLink}\n </a>\n </div>\n </div>\n )}\n </div>\n );\n}\n","import React, { useState, useEffect } from 'react';\nimport { useAuth } from '../providers/AuthProvider';\n\nexport interface MagicLinkVerifyCopy {\n title?: string;\n verifyingMessage?: string;\n successMessage?: string;\n errorMessage?: string;\n redirectingMessage?: string;\n retryButton?: string;\n backToLoginButton?: string;\n}\n\nexport interface MagicLinkVerifyStyles {\n container?: React.CSSProperties;\n card?: React.CSSProperties;\n title?: React.CSSProperties;\n message?: React.CSSProperties;\n successMessage?: React.CSSProperties;\n errorMessage?: React.CSSProperties;\n spinner?: React.CSSProperties;\n buttonContainer?: React.CSSProperties;\n retryButton?: React.CSSProperties;\n backButton?: React.CSSProperties;\n}\n\nexport interface MagicLinkVerifyIcons {\n loading?: React.ReactNode;\n success?: React.ReactNode;\n error?: React.ReactNode;\n}\n\nexport interface MagicLinkVerifyProps {\n copy?: MagicLinkVerifyCopy;\n styles?: MagicLinkVerifyStyles;\n icons?: MagicLinkVerifyIcons;\n onSuccess?: (data: any) => void;\n onError?: (error: string) => void;\n onRetry?: () => void;\n onBackToLogin?: () => void;\n className?: string;\n // Auto-extract from URL params if not provided\n token?: string;\n email?: string;\n appId?: string;\n tenantSlug?: string;\n // Auto-redirect after success (in milliseconds)\n autoRedirectDelay?: number;\n}\n\nconst defaultCopy: Required<MagicLinkVerifyCopy> = {\n title: 'Verifying Magic Link',\n verifyingMessage: 'Please wait while we verify your magic link...',\n successMessage: 'Magic link verified successfully! You are now logged in.',\n errorMessage: 'Failed to verify magic link. The link may be expired or invalid.',\n redirectingMessage: 'Redirecting you to the dashboard...',\n retryButton: 'Try Again',\n backToLoginButton: 'Back to Login',\n};\n\nconst defaultStyles: Required<MagicLinkVerifyStyles> = {\n container: {\n maxWidth: '400px',\n width: '100%',\n margin: '0 auto',\n padding: '2rem',\n backgroundColor: '#ffffff',\n borderRadius: '8px',\n boxShadow: '0 2px 10px rgba(0, 0, 0, 0.1)',\n },\n card: {\n // Not used in new design, keeping for compatibility\n backgroundColor: 'transparent',\n padding: '0',\n borderRadius: '0',\n boxShadow: 'none',\n maxWidth: '100%',\n width: '100%',\n textAlign: 'center',\n },\n title: {\n fontSize: '1.5rem',\n fontWeight: 'bold',\n textAlign: 'center',\n marginBottom: '1.5rem',\n color: '#333333',\n },\n message: {\n fontSize: '1rem',\n color: '#6b7280',\n marginBottom: '1.5rem',\n lineHeight: '1.5',\n textAlign: 'center',\n },\n successMessage: {\n fontSize: '1rem',\n color: '#059669',\n marginBottom: '1.5rem',\n lineHeight: '1.5',\n textAlign: 'center',\n },\n errorMessage: {\n fontSize: '0.875rem',\n color: '#ef4444',\n textAlign: 'center',\n marginBottom: '1rem',\n lineHeight: '1.5',\n },\n spinner: {\n display: 'inline-block',\n width: '20px',\n height: '20px',\n border: '2px solid #e5e7eb',\n borderTop: '2px solid #3b82f6',\n borderRadius: '50%',\n animation: 'spin 1s linear infinite',\n marginRight: '0.5rem',\n },\n buttonContainer: {\n display: 'flex',\n gap: '0.75rem',\n justifyContent: 'center',\n flexWrap: 'wrap',\n marginTop: '1rem',\n },\n retryButton: {\n padding: '0.75rem 1rem',\n backgroundColor: '#3b82f6',\n color: 'white',\n border: 'none',\n borderRadius: '6px',\n fontSize: '1rem',\n fontWeight: '500',\n cursor: 'pointer',\n transition: 'background-color 0.15s ease-in-out',\n },\n backButton: {\n padding: '0.75rem 1rem',\n backgroundColor: '#f3f4f6',\n color: '#374151',\n border: '1px solid #d1d5db',\n borderRadius: '6px',\n fontSize: '1rem',\n fontWeight: '500',\n cursor: 'pointer',\n transition: 'all 0.15s ease-in-out',\n },\n};\n\n// Loading spinner icon\nconst LoadingIcon = () => <div style={defaultStyles.spinner} />;\n\n// Success icon\nconst SuccessIcon = () => (\n <svg\n width=\"48\"\n height=\"48\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"#059669\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n style={{ margin: '0 auto 1rem auto', display: 'block' }}\n >\n <path d=\"M22 11.08V12a10 10 0 1 1-5.93-9.14\" />\n <polyline points=\"22,4 12,14.01 9,11.01\" />\n </svg>\n);\n\n// Error icon\nconst ErrorIcon = () => (\n <svg\n width=\"48\"\n height=\"48\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"#ef4444\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n style={{ margin: '0 auto 1rem auto', display: 'block' }}\n >\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <line x1=\"15\" y1=\"9\" x2=\"9\" y2=\"15\" />\n <line x1=\"9\" y1=\"9\" x2=\"15\" y2=\"15\" />\n </svg>\n);\n\nconst defaultIcons: Required<MagicLinkVerifyIcons> = {\n loading: <LoadingIcon />,\n success: <SuccessIcon />,\n error: <ErrorIcon />,\n};\n\ntype VerificationState = 'verifying' | 'success' | 'error' | 'redirecting';\n\nexport function MagicLinkVerify({\n copy = {},\n styles = {},\n icons = {},\n onSuccess,\n onError,\n onRetry,\n onBackToLogin,\n className,\n token: propToken,\n email: propEmail,\n appId: propAppId,\n tenantSlug: propTenantSlug,\n autoRedirectDelay = 3000,\n}: MagicLinkVerifyProps) {\n const [state, setState] = useState<VerificationState>('verifying');\n const [error, setError] = useState('');\n\n const { verifyMagicLink } = useAuth();\n\n const mergedCopy = { ...defaultCopy, ...copy };\n const mergedStyles = { ...defaultStyles, ...styles };\n const mergedIcons = { ...defaultIcons, ...icons };\n\n // Extract parameters from URL or use props\n const getUrlParams = () => {\n if (typeof window === 'undefined') return {};\n\n const urlParams = new URLSearchParams(window.location.search);\n return {\n token: propToken || urlParams.get('token') || '',\n email: propEmail || urlParams.get('email') || '',\n appId: propAppId || urlParams.get('appId') || '',\n tenantSlug: propTenantSlug || urlParams.get('tenantSlug') || undefined,\n };\n };\n\n const handleVerification = async () => {\n setState('verifying');\n setError('');\n\n try {\n const params = getUrlParams();\n\n if (!params.token || !params.email) {\n throw new Error('Missing required parameters: token or email');\n }\n\n const result = await verifyMagicLink({\n token: params.token,\n email: params.email,\n tenantSlug: params.tenantSlug,\n });\n\n setState('success');\n onSuccess?.(result);\n\n // Auto-redirect after success\n if (autoRedirectDelay > 0) {\n setTimeout(() => {\n setState('redirecting');\n }, autoRedirectDelay);\n }\n } catch (err: any) {\n const errorMessage = err.message || mergedCopy.errorMessage;\n setError(errorMessage);\n setState('error');\n onError?.(errorMessage);\n }\n };\n\n const handleRetry = () => {\n onRetry?.();\n handleVerification();\n };\n\n const handleBackToLogin = () => {\n onBackToLogin?.();\n };\n\n // Auto-verify on mount\n useEffect(() => {\n handleVerification();\n }, []);\n\n const renderContent = () => {\n switch (state) {\n case 'verifying':\n return (\n <div style={mergedStyles.message}>\n {mergedIcons.loading}\n {mergedCopy.verifyingMessage}\n </div>\n );\n\n case 'success':\n return (\n <>\n {mergedIcons.success}\n <div style={mergedStyles.successMessage}>{mergedCopy.successMessage}</div>\n </>\n );\n\n case 'redirecting':\n return (\n <>\n {mergedIcons.loading}\n <div style={mergedStyles.message}>{mergedCopy.redirectingMessage}</div>\n </>\n );\n\n case 'error':\n return (\n <>\n {mergedIcons.error}\n <div style={mergedStyles.errorMessage}>{error || mergedCopy.errorMessage}</div>\n <div style={mergedStyles.buttonContainer}>\n <button\n onClick={handleRetry}\n style={mergedStyles.retryButton}\n onMouseOver={e => {\n e.currentTarget.style.backgroundColor = '#2563eb';\n }}\n onMouseOut={e => {\n e.currentTarget.style.backgroundColor = '#3b82f6';\n }}\n >\n {mergedCopy.retryButton}\n </button>\n <button\n onClick={handleBackToLogin}\n style={mergedStyles.backButton}\n onMouseOver={e => {\n e.currentTarget.style.backgroundColor = '#e5e7eb';\n }}\n onMouseOut={e => {\n e.currentTarget.style.backgroundColor = '#f3f4f6';\n }}\n >\n {mergedCopy.backToLoginButton}\n </button>\n </div>\n </>\n );\n\n default:\n return null;\n }\n };\n\n return (\n <div style={mergedStyles.container} className={className}>\n <style>\n {`\n @keyframes spin {\n 0% { transform: rotate(0deg); }\n 100% { transform: rotate(360deg); }\n }\n `}\n </style>\n <h1 style={mergedStyles.title}>{mergedCopy.title}</h1>\n {renderContent()}\n </div>\n );\n}\n","import React, { useState } from 'react';\nimport { useAuth } from '../providers/AuthProvider';\nimport { useTenantInfo } from '../providers/TenantProvider';\n\nexport interface PasswordRecoveryFormCopy {\n title?: string;\n subtitle?: string;\n emailLabel?: string;\n emailPlaceholder?: string;\n submitButton?: string;\n backToLoginLink?: string;\n successMessage?: string;\n errorMessage?: string;\n loadingText?: string;\n // Reset form copy\n resetTitle?: string;\n resetSubtitle?: string;\n tokenLabel?: string;\n tokenPlaceholder?: string;\n newPasswordLabel?: string;\n newPasswordPlaceholder?: string;\n confirmPasswordLabel?: string;\n confirmPasswordPlaceholder?: string;\n resetSubmitButton?: string;\n resetLoadingText?: string;\n resetSuccessMessage?: string;\n passwordMismatchError?: string;\n}\n\nexport interface PasswordRecoveryFormStyles {\n container?: React.CSSProperties;\n title?: React.CSSProperties;\n subtitle?: React.CSSProperties;\n form?: React.CSSProperties;\n fieldGroup?: React.CSSProperties;\n label?: React.CSSProperties;\n input?: React.CSSProperties;\n inputError?: React.CSSProperties;\n button?: React.CSSProperties;\n buttonDisabled?: React.CSSProperties;\n buttonLoading?: React.CSSProperties;\n errorText?: React.CSSProperties;\n successText?: React.CSSProperties;\n linkContainer?: React.CSSProperties;\n link?: React.CSSProperties;\n}\n\nexport interface PasswordRecoveryFormProps {\n copy?: PasswordRecoveryFormCopy;\n styles?: PasswordRecoveryFormStyles;\n mode?: 'request' | 'reset';\n token?: string;\n onSuccess?: (data?: any) => void;\n onError?: (error: string) => void;\n onBackToLogin?: () => void;\n onModeChange?: (mode: 'request' | 'reset') => void;\n className?: string;\n}\n\nconst defaultCopy: Required<PasswordRecoveryFormCopy> = {\n title: 'Reset Password',\n subtitle: \"Enter your email address and we'll send you a link to reset your password.\",\n emailLabel: 'Email',\n emailPlaceholder: 'Enter your email',\n submitButton: 'Send Reset Link',\n backToLoginLink: 'Back to Sign In',\n successMessage: 'Password reset link sent! Check your email.',\n errorMessage: 'Failed to send reset link',\n loadingText: 'Sending...',\n resetTitle: 'Set New Password',\n resetSubtitle: 'Enter your reset token and new password.',\n tokenLabel: 'Reset Token',\n tokenPlaceholder: 'Enter reset token from email',\n newPasswordLabel: 'New Password',\n newPasswordPlaceholder: 'Enter new password',\n confirmPasswordLabel: 'Confirm Password',\n confirmPasswordPlaceholder: 'Confirm new password',\n resetSubmitButton: 'Reset Password',\n resetLoadingText: 'Resetting...',\n resetSuccessMessage: 'Password reset successfully!',\n passwordMismatchError: 'Passwords do not match',\n};\n\nconst defaultStyles: Required<PasswordRecoveryFormStyles> = {\n container: {\n maxWidth: '400px',\n margin: '0 auto',\n padding: '2rem',\n backgroundColor: '#ffffff',\n borderRadius: '8px',\n boxShadow: '0 2px 10px rgba(0, 0, 0, 0.1)',\n },\n title: {\n fontSize: '1.5rem',\n fontWeight: 'bold',\n textAlign: 'center',\n marginBottom: '0.5rem',\n color: '#333333',\n },\n subtitle: {\n fontSize: '0.875rem',\n textAlign: 'center',\n marginBottom: '1.5rem',\n color: '#6b7280',\n lineHeight: '1.4',\n },\n form: {\n display: 'flex',\n flexDirection: 'column',\n gap: '1rem',\n },\n fieldGroup: {\n display: 'flex',\n flexDirection: 'column',\n gap: '0.5rem',\n },\n label: {\n fontSize: '0.875rem',\n fontWeight: '500',\n color: '#374151',\n },\n input: {\n padding: '0.75rem',\n border: '1px solid #d1d5db',\n borderRadius: '6px',\n fontSize: '1rem',\n transition: 'border-color 0.15s ease-in-out',\n outline: 'none',\n },\n inputError: {\n borderColor: '#ef4444',\n boxShadow: '0 0 0 3px rgba(239, 68, 68, 0.1)',\n },\n button: {\n padding: '0.75rem 1rem',\n backgroundColor: '#f59e0b',\n color: 'white',\n border: 'none',\n borderRadius: '6px',\n fontSize: '1rem',\n fontWeight: '500',\n cursor: 'pointer',\n transition: 'background-color 0.15s ease-in-out',\n marginTop: '0.5rem',\n },\n buttonDisabled: {\n backgroundColor: '#9ca3af',\n cursor: 'not-allowed',\n },\n buttonLoading: {\n backgroundColor: '#6b7280',\n },\n errorText: {\n color: '#ef4444',\n fontSize: '0.875rem',\n textAlign: 'center',\n marginTop: '0.5rem',\n },\n successText: {\n color: '#10b981',\n fontSize: '0.875rem',\n textAlign: 'center',\n marginTop: '0.5rem',\n },\n linkContainer: {\n textAlign: 'center',\n marginTop: '1rem',\n },\n link: {\n color: '#3b82f6',\n textDecoration: 'none',\n fontSize: '0.875rem',\n cursor: 'pointer',\n },\n};\n\nexport function PasswordRecoveryForm({\n copy = {},\n styles = {},\n mode = 'request',\n token: initialToken = '',\n onSuccess,\n onError,\n onBackToLogin,\n onModeChange,\n className,\n}: PasswordRecoveryFormProps) {\n const [email, setEmail] = useState('');\n const [token, setToken] = useState(initialToken);\n const [newPassword, setNewPassword] = useState('');\n const [confirmPassword, setConfirmPassword] = useState('');\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState('');\n const [success, setSuccess] = useState('');\n const [fieldErrors, setFieldErrors] = useState<{\n email?: boolean;\n token?: boolean;\n newPassword?: boolean;\n confirmPassword?: boolean;\n }>({});\n\n const { requestPasswordReset, confirmPasswordReset } = useAuth();\n const { tenant } = useTenantInfo();\n\n const mergedCopy = { ...defaultCopy, ...copy };\n const mergedStyles = { ...defaultStyles, ...styles };\n\n const validateRequestForm = () => {\n const errors: { email?: boolean } = {};\n if (!email.trim()) errors.email = true;\n setFieldErrors(errors);\n return Object.keys(errors).length === 0;\n };\n\n const validateResetForm = () => {\n const errors: { token?: boolean; newPassword?: boolean; confirmPassword?: boolean } = {};\n if (!token.trim()) errors.token = true;\n if (!newPassword.trim()) errors.newPassword = true;\n if (!confirmPassword.trim()) errors.confirmPassword = true;\n setFieldErrors(errors);\n return Object.keys(errors).length === 0;\n };\n\n const handleRequestSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n\n if (!validateRequestForm()) return;\n if (!tenant?.id) {\n setError('Tenant not found');\n return;\n }\n\n setLoading(true);\n setError('');\n setSuccess('');\n\n try {\n await requestPasswordReset({ email, tenantId: tenant.id });\n setSuccess(mergedCopy.successMessage);\n onSuccess?.();\n } catch (err: any) {\n const errorMessage = err.message || mergedCopy.errorMessage;\n setError(errorMessage);\n onError?.(errorMessage);\n } finally {\n setLoading(false);\n }\n };\n\n const handleResetSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n\n if (!validateResetForm()) return;\n\n if (newPassword !== confirmPassword) {\n setError(mergedCopy.passwordMismatchError);\n setFieldErrors({ confirmPassword: true });\n return;\n }\n\n setLoading(true);\n setError('');\n setSuccess('');\n\n try {\n await confirmPasswordReset({ token, newPassword });\n setSuccess(mergedCopy.resetSuccessMessage);\n onSuccess?.();\n } catch (err: any) {\n const errorMessage = err.message || mergedCopy.errorMessage;\n setError(errorMessage);\n onError?.(errorMessage);\n } finally {\n setLoading(false);\n }\n };\n\n const getInputStyle = (field: keyof typeof fieldErrors) => ({\n ...mergedStyles.input,\n ...(fieldErrors[field] ? mergedStyles.inputError : {}),\n });\n\n const getButtonStyle = () => ({\n ...mergedStyles.button,\n ...(loading ? mergedStyles.buttonLoading : {}),\n });\n\n if (mode === 'reset') {\n const isFormValid = token && newPassword && confirmPassword;\n\n return (\n <div className={className} style={mergedStyles.container}>\n <h2 style={mergedStyles.title}>{mergedCopy.resetTitle}</h2>\n <p style={mergedStyles.subtitle}>{mergedCopy.resetSubtitle}</p>\n\n <form onSubmit={handleResetSubmit} style={mergedStyles.form}>\n <div style={mergedStyles.fieldGroup}>\n <label style={mergedStyles.label}>{mergedCopy.tokenLabel}</label>\n <input\n type=\"text\"\n value={token}\n onChange={e => {\n setToken(e.target.value);\n if (fieldErrors.token) {\n setFieldErrors(prev => ({ ...prev, token: false }));\n }\n }}\n placeholder={mergedCopy.tokenPlaceholder}\n style={getInputStyle('token')}\n disabled={loading}\n />\n </div>\n\n <div style={mergedStyles.fieldGroup}>\n <label style={mergedStyles.label}>{mergedCopy.newPasswordLabel}</label>\n <input\n type=\"password\"\n value={newPassword}\n onChange={e => {\n setNewPassword(e.target.value);\n if (fieldErrors.newPassword) {\n setFieldErrors(prev => ({ ...prev, newPassword: false }));\n }\n }}\n placeholder={mergedCopy.newPasswordPlaceholder}\n style={getInputStyle('newPassword')}\n disabled={loading}\n />\n </div>\n\n <div style={mergedStyles.fieldGroup}>\n <label style={mergedStyles.label}>{mergedCopy.confirmPasswordLabel}</label>\n <input\n type=\"password\"\n value={confirmPassword}\n onChange={e => {\n setConfirmPassword(e.target.value);\n if (fieldErrors.confirmPassword) {\n setFieldErrors(prev => ({ ...prev, confirmPassword: false }));\n }\n if (error === mergedCopy.passwordMismatchError) {\n setError('');\n }\n }}\n placeholder={mergedCopy.confirmPasswordPlaceholder}\n style={getInputStyle('confirmPassword')}\n disabled={loading}\n />\n </div>\n\n <button\n type=\"submit\"\n disabled={!isFormValid || loading}\n style={{\n ...getButtonStyle(),\n ...(!isFormValid || loading ? mergedStyles.buttonDisabled : {}),\n }}\n >\n {loading ? mergedCopy.resetLoadingText : mergedCopy.resetSubmitButton}\n </button>\n\n {error && <div style={mergedStyles.errorText}>{error}</div>}\n {success && <div style={mergedStyles.successText}>{success}</div>}\n </form>\n\n <div style={mergedStyles.linkContainer}>\n <a onClick={onBackToLogin} style={mergedStyles.link}>\n {mergedCopy.backToLoginLink}\n </a>\n {onModeChange && (\n <>\n <span style={{ margin: '0 0.5rem', color: '#6b7280' }}>•</span>\n <a onClick={() => onModeChange('request')} style={mergedStyles.link}>\n Request New Link\n </a>\n </>\n )}\n </div>\n </div>\n );\n }\n\n // Request mode\n const isFormValid = email;\n\n return (\n <div className={className} style={mergedStyles.container}>\n <h2 style={mergedStyles.title}>{mergedCopy.title}</h2>\n <p style={mergedStyles.subtitle}>{mergedCopy.subtitle}</p>\n\n <form onSubmit={handleRequestSubmit} style={mergedStyles.form}>\n <div style={mergedStyles.fieldGroup}>\n <label style={mergedStyles.label}>{mergedCopy.emailLabel}</label>\n <input\n type=\"email\"\n value={email}\n onChange={e => {\n setEmail(e.target.value);\n if (fieldErrors.email) {\n setFieldErrors(prev => ({ ...prev, email: false }));\n }\n }}\n placeholder={mergedCopy.emailPlaceholder}\n style={getInputStyle('email')}\n disabled={loading}\n />\n </div>\n\n <button\n type=\"submit\"\n disabled={!isFormValid || loading}\n style={{\n ...getButtonStyle(),\n ...(!isFormValid || loading ? mergedStyles.buttonDisabled : {}),\n }}\n >\n {loading ? mergedCopy.loadingText : mergedCopy.submitButton}\n </button>\n\n {error && <div style={mergedStyles.errorText}>{error}</div>}\n {success && <div style={mergedStyles.successText}>{success}</div>}\n </form>\n\n <div style={mergedStyles.linkContainer}>\n <a onClick={onBackToLogin} style={mergedStyles.link}>\n {mergedCopy.backToLoginLink}\n </a>\n {onModeChange && (\n <>\n <span style={{ margin: '0 0.5rem', color: '#6b7280' }}>•</span>\n <a onClick={() => onModeChange('reset')} style={mergedStyles.link}>\n I have a token\n </a>\n </>\n )}\n </div>\n </div>\n );\n}\n","import { ReactNode } from 'react';\nimport { useApp } from '../providers/AppProvider';\nimport { useTenantOptional } from '../providers/TenantProvider';\nimport { useAuthOptional } from '../providers/AuthProvider';\nimport { useFeatureFlagsOptional } from '../providers/FeatureFlagProvider';\nimport { useSubscriptionOptional } from '../providers/SubscriptionProvider';\n\nexport interface AppLoaderProps {\n children: ReactNode;\n loadingFallback?: ReactNode;\n errorFallback?: ReactNode | ((error: Error, retry: () => void) => ReactNode);\n // Optional: require tenant to be loaded (default: true)\n requireTenant?: boolean;\n}\n\n// Default loading component\nconst DefaultLoadingFallback = () => (\n <div\n style={{\n display: 'flex',\n justifyContent: 'center',\n alignItems: 'center',\n height: '100vh',\n fontFamily: 'system-ui, sans-serif',\n }}\n >\n <div>Loading...</div>\n </div>\n);\n\n// Default error component\nconst DefaultErrorFallback = ({ error, retry }: { error: Error; retry: () => void }) => (\n <div\n style={{\n display: 'flex',\n flexDirection: 'column',\n justifyContent: 'center',\n alignItems: 'center',\n height: '100vh',\n fontFamily: 'system-ui, sans-serif',\n textAlign: 'center',\n padding: '20px',\n }}\n >\n <h2 style={{ color: '#dc3545', marginBottom: '16px' }}>Error</h2>\n <p style={{ color: '#6c757d', marginBottom: '24px' }}>\n {error.message || 'Unable to load application'}\n </p>\n <button\n onClick={retry}\n style={{\n padding: '8px 16px',\n backgroundColor: '#007bff',\n color: 'white',\n border: 'none',\n borderRadius: '4px',\n cursor: 'pointer',\n }}\n >\n Retry\n </button>\n </div>\n);\n\n/**\n * AppLoader - Blocks rendering until App (and optionally Tenant) data are loaded\n *\n * Must be used inside AppProvider. TenantProvider is optional.\n *\n * @example\n * ```tsx\n * // With TenantProvider (parallel loading)\n * <AppProvider config={appConfig}>\n * <TenantProvider config={tenantConfig}>\n * <AppLoader loadingFallback={<Spinner />}>\n * <App />\n * </AppLoader>\n * </TenantProvider>\n * </AppProvider>\n *\n * // Without TenantProvider (app only)\n * <AppProvider config={appConfig}>\n * <AppLoader loadingFallback={<Spinner />}>\n * <App />\n * </AppLoader>\n * </AppProvider>\n * ```\n */\nexport function AppLoader({\n children,\n loadingFallback,\n errorFallback,\n requireTenant = true,\n}: AppLoaderProps) {\n // AppProvider is required - useApp will throw if not present\n const { isAppLoading, appError, retryApp } = useApp();\n\n // TenantProvider is optional\n const tenantContext = useTenantOptional();\n\n // Optional providers\n const authContext = useAuthOptional();\n const featureFlagsContext = useFeatureFlagsOptional();\n const subscriptionContext = useSubscriptionOptional();\n\n // Extract tenant state (if TenantProvider is present)\n const isTenantLoading = tenantContext?.isTenantLoading ?? false;\n const tenantError = tenantContext?.tenantError ?? null;\n const tenantSlug = tenantContext?.tenantSlug ?? null;\n const retryTenant = tenantContext?.retryTenant ?? (() => {});\n\n // Extract ready states from optional providers (default to ready if not present)\n const isAuthReady = authContext?.isAuthReady ?? true;\n const isFeatureFlagsReady = featureFlagsContext?.isReady ?? true;\n const isSubscriptionReady = subscriptionContext?.isReady ?? true;\n\n // Determine if we're still loading\n // Only wait for tenant if: requireTenant is true AND TenantProvider exists AND there's a tenantSlug\n const shouldWaitForTenant = requireTenant && tenantContext && tenantSlug;\n // Wait for optional providers if they exist and are not ready\n const shouldWaitForAuth = authContext && !isAuthReady;\n const shouldWaitForFeatureFlags = featureFlagsContext && !isFeatureFlagsReady;\n const shouldWaitForSubscription = subscriptionContext && !isSubscriptionReady;\n\n const isLoading =\n isAppLoading ||\n (shouldWaitForTenant && isTenantLoading) ||\n shouldWaitForAuth ||\n shouldWaitForFeatureFlags ||\n shouldWaitForSubscription;\n\n // Combine errors - app error takes priority\n const error = appError || (shouldWaitForTenant ? tenantError : null);\n\n // Combined retry function\n const retry = () => {\n if (appError) {\n retryApp();\n }\n if (tenantError && tenantContext) {\n retryTenant();\n }\n };\n\n // Show loading state\n if (isLoading) {\n return <>{loadingFallback || <DefaultLoadingFallback />}</>;\n }\n\n // Show error state\n if (error) {\n const ErrorComponent =\n typeof errorFallback === 'function'\n ? errorFallback(error, retry)\n : errorFallback || <DefaultErrorFallback error={error} retry={retry} />;\n\n return <>{ErrorComponent}</>;\n }\n\n // Both app and tenant are ready\n return <>{children}</>;\n}\n\n/**\n * Hook to get the combined loading state of App and Tenant\n * Useful for showing loading indicators without blocking rendering\n *\n * Must be used inside AppProvider. TenantProvider is optional.\n */\nexport function useAppLoaderState(requireTenant = true) {\n // AppProvider is required - useApp will throw if not present\n const { isAppLoading, appError, retryApp, appInfo } = useApp();\n\n // Optional providers\n const tenantContext = useTenantOptional();\n const authContext = useAuthOptional();\n const featureFlagsContext = useFeatureFlagsOptional();\n const subscriptionContext = useSubscriptionOptional();\n\n const isTenantLoading = tenantContext?.isTenantLoading ?? false;\n const tenantError = tenantContext?.tenantError ?? null;\n const tenant = tenantContext?.tenant ?? null;\n const tenantSlug = tenantContext?.tenantSlug ?? null;\n const retryTenant = tenantContext?.retryTenant ?? (() => {});\n\n const isAuthReady = authContext?.isAuthReady ?? true;\n const isFeatureFlagsReady = featureFlagsContext?.isReady ?? true;\n const isSubscriptionReady = subscriptionContext?.isReady ?? true;\n\n const shouldWaitForTenant = requireTenant && tenantContext && tenantSlug;\n const shouldWaitForAuth = authContext && !isAuthReady;\n const shouldWaitForFeatureFlags = featureFlagsContext && !isFeatureFlagsReady;\n const shouldWaitForSubscription = subscriptionContext && !isSubscriptionReady;\n\n const isLoading =\n isAppLoading ||\n (shouldWaitForTenant && isTenantLoading) ||\n shouldWaitForAuth ||\n shouldWaitForFeatureFlags ||\n shouldWaitForSubscription;\n\n const error = appError || (shouldWaitForTenant ? tenantError : null);\n const isReady =\n !isLoading && !error && appInfo !== null && (!shouldWaitForTenant || tenant !== null);\n\n const retry = () => {\n if (appError) retryApp();\n if (tenantError && tenantContext) retryTenant();\n };\n\n return {\n isLoading,\n error,\n isReady,\n retry,\n // Individual states\n app: { isLoading: isAppLoading, error: appError, data: appInfo },\n tenant: tenantContext ? { isLoading: isTenantLoading, error: tenantError, data: tenant } : null,\n auth: authContext ? { isReady: isAuthReady } : null,\n featureFlags: featureFlagsContext ? { isReady: isFeatureFlagsReady } : null,\n subscription: subscriptionContext ? { isReady: isSubscriptionReady } : null,\n };\n}\n","import React, { useState, useRef, useEffect } from 'react';\nimport { useAuthOptional } from '../providers/AuthProvider';\nimport type { UserTenantMembership } from '../types/api';\n\nexport interface TenantSelectorProps {\n tenants?: UserTenantMembership[];\n currentTenantId?: string | null;\n onSelect?: (tenantId: string) => void;\n className?: string;\n dropdownClassName?: string;\n itemClassName?: string;\n renderItem?: (tenant: UserTenantMembership, isSelected: boolean) => React.ReactNode;\n placeholder?: string;\n disabled?: boolean;\n showCurrentTenant?: boolean;\n}\n\nexport function TenantSelector({\n tenants: propTenants,\n currentTenantId: propCurrentTenantId,\n onSelect: propOnSelect,\n className = '',\n dropdownClassName = '',\n itemClassName = '',\n renderItem,\n placeholder = 'Select tenant',\n disabled = false,\n showCurrentTenant = true,\n}: TenantSelectorProps) {\n const auth = useAuthOptional();\n const [isOpen, setIsOpen] = useState(false);\n const dropdownRef = useRef<HTMLDivElement>(null);\n\n // Use props if provided, otherwise fall back to context\n const tenants = propTenants ?? auth?.userTenants ?? [];\n const currentTenantId = propCurrentTenantId ?? auth?.currentUser?.tenantId ?? null;\n\n const handleSelect = async (tenantId: string) => {\n setIsOpen(false);\n if (propOnSelect) {\n propOnSelect(tenantId);\n } else if (auth?.switchToTenant) {\n await auth.switchToTenant(tenantId);\n }\n };\n\n // Close dropdown when clicking outside\n useEffect(() => {\n const handleClickOutside = (event: MouseEvent) => {\n if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {\n setIsOpen(false);\n }\n };\n\n document.addEventListener('mousedown', handleClickOutside);\n return () => document.removeEventListener('mousedown', handleClickOutside);\n }, []);\n\n // Find current tenant\n const currentTenant = tenants.find(t => t.id === currentTenantId);\n\n // Don't render if no tenants available\n if (tenants.length === 0) {\n return null;\n }\n\n // Don't render selector if only one tenant and showing current\n if (tenants.length === 1 && showCurrentTenant) {\n return (\n <div className={className}>\n <span>{tenants[0].name}</span>\n </div>\n );\n }\n\n const defaultRenderItem = (tenant: UserTenantMembership, isSelected: boolean) => (\n <span style={{ fontWeight: isSelected ? 'bold' : 'normal' }}>\n {tenant.name}\n {tenant.role && <span style={{ opacity: 0.7, marginLeft: 8 }}>({tenant.role})</span>}\n </span>\n );\n\n return (\n <div ref={dropdownRef} className={className} style={{ position: 'relative' }}>\n <button\n type=\"button\"\n onClick={() => !disabled && setIsOpen(!isOpen)}\n disabled={disabled}\n style={{\n cursor: disabled ? 'not-allowed' : 'pointer',\n opacity: disabled ? 0.6 : 1,\n }}\n >\n {currentTenant ? currentTenant.name : placeholder}\n <span style={{ marginLeft: 8 }}>{isOpen ? '▲' : '▼'}</span>\n </button>\n\n {isOpen && (\n <div\n className={dropdownClassName}\n style={{\n position: 'absolute',\n top: '100%',\n left: 0,\n right: 0,\n zIndex: 1000,\n backgroundColor: 'white',\n border: '1px solid #ccc',\n borderRadius: 4,\n boxShadow: '0 2px 8px rgba(0,0,0,0.15)',\n maxHeight: 300,\n overflowY: 'auto',\n }}\n >\n {tenants.map(tenant => {\n const isSelected = tenant.id === currentTenantId;\n return (\n <div\n key={tenant.id}\n className={itemClassName}\n onClick={() => handleSelect(tenant.id)}\n style={{\n padding: '8px 12px',\n cursor: 'pointer',\n backgroundColor: isSelected ? '#f0f0f0' : 'transparent',\n }}\n onMouseEnter={e => {\n if (!isSelected) {\n (e.target as HTMLElement).style.backgroundColor = '#f5f5f5';\n }\n }}\n onMouseLeave={e => {\n if (!isSelected) {\n (e.target as HTMLElement).style.backgroundColor = 'transparent';\n }\n }}\n >\n {renderItem\n ? renderItem(tenant, isSelected)\n : defaultRenderItem(tenant, isSelected)}\n </div>\n );\n })}\n </div>\n )}\n </div>\n );\n}\n\nexport default TenantSelector;\n","import { HttpService } from './HttpService';\nimport { SessionManager } from './SessionManager';\nimport type {\n Permission,\n CreatePermissionRequest,\n ApiResponse,\n PaginationParams,\n} from '../types/api';\n\nexport class PermissionApiService {\n constructor(\n private httpService: HttpService,\n private sessionManager?: SessionManager\n ) {}\n\n async createPermission(request: CreatePermissionRequest): Promise<Permission> {\n if (!this.sessionManager) {\n throw new Error('SessionManager is required for private endpoints');\n }\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const response = await this.httpService.post<ApiResponse<Permission>>(\n '/permissions/',\n request,\n {\n headers: authHeaders,\n }\n );\n return response.data;\n }\n\n async getPermissions(\n params?: PaginationParams\n ): Promise<{ permissions: Permission[]; meta: any }> {\n if (!this.sessionManager) {\n throw new Error('SessionManager is required for private endpoints');\n }\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const queryParams = new URLSearchParams();\n\n if (params?.page) queryParams.append('page', params.page.toString());\n if (params?.limit) queryParams.append('limit', params.limit.toString());\n if (params?.sortBy) queryParams.append('sortBy', params.sortBy);\n if (params?.sortOrder) queryParams.append('sortOrder', params.sortOrder);\n\n const url = `/permissions/${queryParams.toString() ? `?${queryParams.toString()}` : ''}`;\n const response = await this.httpService.get<ApiResponse<Permission[]>>(url, {\n headers: authHeaders,\n });\n\n return {\n permissions: response.data,\n meta: response.meta,\n };\n }\n\n async getPermissionById(id: string): Promise<Permission> {\n if (!this.sessionManager) {\n throw new Error('SessionManager is required for private endpoints');\n }\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const response = await this.httpService.get<ApiResponse<Permission>>(`/permissions/${id}`, {\n headers: authHeaders,\n });\n return response.data;\n }\n\n async updatePermission(\n id: string,\n request: Partial<CreatePermissionRequest>\n ): Promise<Permission> {\n if (!this.sessionManager) {\n throw new Error('SessionManager is required for private endpoints');\n }\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const response = await this.httpService.put<ApiResponse<Permission>>(\n `/permissions/${id}`,\n request,\n {\n headers: authHeaders,\n }\n );\n return response.data;\n }\n\n async deletePermission(id: string): Promise<void> {\n if (!this.sessionManager) {\n throw new Error('SessionManager is required for private endpoints');\n }\n const authHeaders = await this.sessionManager.getAuthHeaders();\n await this.httpService.delete<void>(`/permissions/${id}`, {\n headers: authHeaders,\n });\n }\n\n // Public endpoint - no auth required\n async getAppPermissions(\n appId: string,\n params?: PaginationParams\n ): Promise<{ permissions: Permission[]; meta: any }> {\n const queryParams = new URLSearchParams();\n\n if (params?.page) queryParams.append('page', params.page.toString());\n if (params?.limit) queryParams.append('limit', params.limit.toString());\n if (params?.sortBy) queryParams.append('sortBy', params.sortBy);\n if (params?.sortOrder) queryParams.append('sortOrder', params.sortOrder);\n\n const url = `/permissions/apps/${appId}${queryParams.toString() ? `?${queryParams.toString()}` : ''}`;\n const response = await this.httpService.get<ApiResponse<Permission[]>>(url);\n\n return {\n permissions: response.data,\n meta: response.meta,\n };\n }\n}\n","import { HttpService } from './HttpService';\nimport { SessionManager } from './SessionManager';\nimport type {\n SubscriptionPlan,\n CreateSubscriptionPlanRequest,\n ApiResponse,\n PaginationParams,\n} from '../types/api';\n\nexport class SubscriptionPlanApiService {\n constructor(\n private httpService: HttpService,\n private sessionManager: SessionManager\n ) {}\n\n async createSubscriptionPlan(request: CreateSubscriptionPlanRequest): Promise<SubscriptionPlan> {\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const response = await this.httpService.post<ApiResponse<SubscriptionPlan>>(\n '/subscription-plans/',\n request,\n {\n headers: authHeaders,\n }\n );\n return response.data;\n }\n\n async getSubscriptionPlans(\n params?: PaginationParams & { appId?: string }\n ): Promise<{ plans: SubscriptionPlan[]; meta: any }> {\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const queryParams = new URLSearchParams();\n\n if (params?.page) queryParams.append('page', params.page.toString());\n if (params?.limit) queryParams.append('limit', params.limit.toString());\n if (params?.sortBy) queryParams.append('sortBy', params.sortBy);\n if (params?.sortOrder) queryParams.append('sortOrder', params.sortOrder);\n if (params?.appId) queryParams.append('appId', params.appId);\n\n const url = `/subscription-plans/${queryParams.toString() ? `?${queryParams.toString()}` : ''}`;\n const response = await this.httpService.get<ApiResponse<SubscriptionPlan[]>>(url, {\n headers: authHeaders,\n });\n\n return {\n plans: response.data,\n meta: response.meta,\n };\n }\n\n async getSubscriptionPlanById(id: string): Promise<SubscriptionPlan> {\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const response = await this.httpService.get<ApiResponse<SubscriptionPlan>>(\n `/subscription-plans/${id}`,\n {\n headers: authHeaders,\n }\n );\n return response.data;\n }\n\n async updateSubscriptionPlan(\n id: string,\n request: Partial<CreateSubscriptionPlanRequest>\n ): Promise<SubscriptionPlan> {\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const response = await this.httpService.put<ApiResponse<SubscriptionPlan>>(\n `/subscription-plans/${id}`,\n request,\n {\n headers: authHeaders,\n }\n );\n return response.data;\n }\n\n async deleteSubscriptionPlan(id: string): Promise<void> {\n const authHeaders = await this.sessionManager.getAuthHeaders();\n await this.httpService.delete<void>(`/subscription-plans/${id}`, {\n headers: authHeaders,\n });\n }\n}\n","import { HttpService } from './HttpService';\n\nexport class HealthApiService {\n constructor(private httpService: HttpService) {}\n\n // Public endpoint - no auth required\n async checkHealth(): Promise<{ status: string }> {\n return await this.httpService.get<{ status: string }>('/health');\n }\n}\n","// Data transformation utilities for API responses\n\nexport class ApiMappers {\n // Date string to Date object\n static toDate(dateString: string): Date {\n return new Date(dateString);\n }\n\n // Date object to ISO string\n static toISOString(date: Date): string {\n return date.toISOString();\n }\n\n // Transform API response pagination meta\n static transformPaginationMeta(meta: any) {\n return {\n total: meta.total || 0,\n page: meta.page || 1,\n limit: meta.limit || 100,\n totalPages: meta.totalPages || 1,\n hasNext: meta.hasNext || false,\n hasPrev: meta.hasPrev || false,\n };\n }\n\n // Transform user data for display\n static transformUser(user: any) {\n return {\n ...user,\n createdAt: this.toDate(user.createdAt),\n updatedAt: this.toDate(user.updatedAt),\n displayName: user.lastName ? `${user.name} ${user.lastName}` : user.name,\n isActiveUser: user.isActive,\n };\n }\n\n // Transform role data for display\n static transformRole(role: any) {\n return {\n ...role,\n createdAt: this.toDate(role.createdAt),\n updatedAt: this.toDate(role.updatedAt),\n permissionCount: role.permissions?.length || 0,\n };\n }\n\n // Transform tenant data for display\n static transformTenant(tenant: any) {\n return {\n ...tenant,\n createdAt: this.toDate(tenant.createdAt),\n updatedAt: this.toDate(tenant.updatedAt),\n displayName: tenant.name,\n hasCustomDomain: !!tenant.domain,\n };\n }\n\n // Transform subscription data for display\n static transformSubscription(subscription: any) {\n return {\n ...subscription,\n createdAt: this.toDate(subscription.createdAt),\n updatedAt: this.toDate(subscription.updatedAt),\n startDate: this.toDate(subscription.startDate),\n endDate: subscription.endDate ? this.toDate(subscription.endDate) : null,\n isActive: subscription.status === 'ACTIVE',\n isExpired: subscription.endDate ? new Date(subscription.endDate) < new Date() : false,\n };\n }\n\n // Transform app data for display\n static transformApp(app: any) {\n return {\n ...app,\n createdAt: this.toDate(app.createdAt),\n updatedAt: this.toDate(app.updatedAt),\n isAdminLevel: app.securityLevel === 'ADMIN',\n hasDefaultPlan: !!app.defaultSubscriptionPlanId,\n };\n }\n\n // Transform feature flag data for display\n static transformFeatureFlag(featureFlag: any) {\n return {\n ...featureFlag,\n createdAt: this.toDate(featureFlag.createdAt),\n updatedAt: this.toDate(featureFlag.updatedAt),\n isEnabled: featureFlag.isActive,\n };\n }\n\n // Transform permission data for display\n static transformPermission(permission: any) {\n return {\n ...permission,\n createdAt: this.toDate(permission.createdAt),\n updatedAt: this.toDate(permission.updatedAt),\n fullName: `${permission.resource}:${permission.action}`,\n isSystemLevel: !permission.appId,\n };\n }\n\n // Transform subscription plan data for display\n static transformSubscriptionPlan(plan: any) {\n return {\n ...plan,\n createdAt: this.toDate(plan.createdAt),\n updatedAt: this.toDate(plan.updatedAt),\n displayPrice: `${plan.currency} ${plan.price}`,\n isMonthly: plan.billingCycle === 'MONTHLY',\n featureCount: plan.features?.length || 0,\n };\n }\n\n // Transform error response\n static transformError(error: any) {\n return {\n code: error.error?.code || 'UNKNOWN_ERROR',\n message: error.message || 'An unexpected error occurred',\n type: error.type || 'SYSTEM',\n isAuthError: error.type === 'AUTH',\n isValidationError: error.type === 'VALIDATION',\n };\n }\n\n // Transform query parameters for API calls\n static transformQueryParams(params: any): URLSearchParams {\n const searchParams = new URLSearchParams();\n\n Object.entries(params).forEach(([key, value]) => {\n if (value !== undefined && value !== null && value !== '') {\n searchParams.append(key, String(value));\n }\n });\n\n return searchParams;\n }\n}\n","import { useCallback, useMemo } from 'react';\nimport { useNavigate, useSearchParams } from 'react-router-dom';\nimport { useAuth } from '../providers/AuthProvider';\nimport { useTenant } from '../providers/TenantProvider';\nimport { UserType } from '../types/api';\nimport {\n DEFAULT_ZONE_ROOTS,\n ReturnToStorage,\n ZoneRoots,\n UseZoneNavigationReturn,\n} from '../types/zoneRouting';\n\nconst DEFAULT_RETURN_TO_PARAM = 'returnTo';\nconst RETURN_TO_SESSION_KEY = 'zone_return_to';\nconst RETURN_TO_LOCAL_KEY = 'zone_return_to';\n\ninterface UseZoneNavigationOptions {\n zoneRoots?: ZoneRoots;\n returnToParam?: string;\n returnToStorage?: ReturnToStorage;\n}\n\n/**\n * Hook for zone-based navigation utilities\n */\nexport function useZoneNavigation(options: UseZoneNavigationOptions = {}): UseZoneNavigationReturn {\n const {\n zoneRoots = {},\n returnToParam = DEFAULT_RETURN_TO_PARAM,\n returnToStorage = 'url',\n } = options;\n\n const navigate = useNavigate();\n const [searchParams, setSearchParams] = useSearchParams();\n const { isAuthenticated, currentUser } = useAuth();\n const { tenant } = useTenant();\n\n const mergedZoneRoots = useMemo(() => ({ ...DEFAULT_ZONE_ROOTS, ...zoneRoots }), [zoneRoots]);\n\n const hasTenant = Boolean(tenant);\n const userType = currentUser?.userType;\n\n /**\n * Get return URL from storage\n */\n const returnToUrl = useMemo((): string | null => {\n switch (returnToStorage) {\n case 'url':\n return searchParams.get(returnToParam);\n case 'session':\n return sessionStorage.getItem(RETURN_TO_SESSION_KEY);\n case 'local':\n return localStorage.getItem(RETURN_TO_LOCAL_KEY);\n default:\n return null;\n }\n }, [returnToStorage, searchParams, returnToParam]);\n\n /**\n * Clear return URL from storage\n */\n const clearReturnTo = useCallback(() => {\n switch (returnToStorage) {\n case 'url': {\n const newParams = new URLSearchParams(searchParams);\n newParams.delete(returnToParam);\n setSearchParams(newParams, { replace: true });\n break;\n }\n case 'session':\n sessionStorage.removeItem(RETURN_TO_SESSION_KEY);\n break;\n case 'local':\n localStorage.removeItem(RETURN_TO_LOCAL_KEY);\n break;\n }\n }, [returnToStorage, searchParams, returnToParam, setSearchParams]);\n\n /**\n * Set return URL in storage\n */\n const setReturnTo = useCallback(\n (url: string) => {\n switch (returnToStorage) {\n case 'url': {\n const newParams = new URLSearchParams(searchParams);\n newParams.set(returnToParam, url);\n setSearchParams(newParams, { replace: true });\n break;\n }\n case 'session':\n sessionStorage.setItem(RETURN_TO_SESSION_KEY, url);\n break;\n case 'local':\n localStorage.setItem(RETURN_TO_LOCAL_KEY, url);\n break;\n }\n },\n [returnToStorage, searchParams, returnToParam, setSearchParams]\n );\n\n /**\n * Navigate to a specific zone root\n */\n const navigateToZone = useCallback(\n (zone: keyof ZoneRoots) => {\n const path = mergedZoneRoots[zone] || mergedZoneRoots.default;\n navigate(path);\n },\n [navigate, mergedZoneRoots]\n );\n\n /**\n * Get smart redirect based on current state\n */\n const getSmartRedirect = useCallback((): string => {\n if (!hasTenant) {\n // No tenant context\n if (!isAuthenticated) {\n return mergedZoneRoots.publicGuest;\n }\n if (userType === UserType.TENANT_ADMIN) {\n return mergedZoneRoots.publicAdmin;\n }\n return mergedZoneRoots.publicUser;\n } else {\n // Has tenant context\n if (!isAuthenticated) {\n return mergedZoneRoots.tenantGuest;\n }\n if (userType === UserType.TENANT_ADMIN) {\n return mergedZoneRoots.tenantAdmin;\n }\n return mergedZoneRoots.tenantUser;\n }\n }, [hasTenant, isAuthenticated, userType, mergedZoneRoots]);\n\n return {\n returnToUrl,\n clearReturnTo,\n setReturnTo,\n navigateToZone,\n getSmartRedirect,\n };\n}\n\n/**\n * Build redirect URL with return path\n */\nexport function buildRedirectUrl(\n redirectTo: string,\n returnPath: string | null,\n returnToParam: string = DEFAULT_RETURN_TO_PARAM,\n returnToStorage: ReturnToStorage = 'url'\n): string {\n if (!returnPath || returnToStorage !== 'url') {\n return redirectTo;\n }\n\n const url = new URL(redirectTo, window.location.origin);\n url.searchParams.set(returnToParam, returnPath);\n return url.pathname + url.search;\n}\n"],"names":["HttpService","baseUrl","timeout","sessionManager","method","endpoint","data","options","url","requestTimeout","requestHeaders","accessToken","controller","timeoutId","response","contentType","error","AppApiService","httpService","request","authHeaders","params","queryParams","id","appId","planId","schema","defaultSettings","AppContext","createContext","AppProvider","config","children","cacheConfig","useMemo","_a","_b","_c","appInfo","setAppInfo","useState","cached","parsed","isAppLoading","setIsAppLoading","appError","setAppError","contextValue","retryApp","loadApp","useCallback","bypassCache","appData","cacheData","err","backgroundRefresh","useEffect","useApp","context","useContext","useAppOptional","useApi","SessionExpiredError","reason","message","defaultMessages","TokenRefreshTimeoutError","timeoutMs","TokenRefreshError","attempts","lastError","_SessionManager","key","existing","instance","storageKey","stored","parts","payload","tokens","expiresAt","tokenData","currentData","refreshToken","expiresIn","tokenType","resolvedExpiresAt","token","delay","gen","resolve","reject","idx","e","newTokens","newAccessToken","queue","entry","attempt","backoff","networkError","errorMessage","body","refreshResponse","user","expiredError","decodedPayload","ms","SessionManager","AuthApiService","RoleApiService","roleId","userId","UserApiService","TenantApiService","slug","detectSubdomainTenant","hostname","baseDomain","baseDomainLower","currentHost","subdomain","detectSelectorTenant","search","selectorParam","localStorage","urlTenant","detectTenantSlug","location","tenantMode","fixedTenantSlug","buildTenantHostname","targetTenantSlug","currentHostname","AUTH_TRANSFER_PARAM","encodeAuthTokens","decodeAuthTokens","encoded","base64","extractAuthTokensFromUrl","decoded","clearAuthTokensFromUrl","TenantContext","TenantProvider","detectTenant","tenantSlug","setTenantSlug","tenant","setTenant","isTenantLoading","setIsTenantLoading","tenantError","setTenantError","settings","setSettings","isSettingsLoading","setIsSettingsLoading","settingsError","setSettingsError","detected","settingsSchema","loadTenant","tenantInfo","loadSettings","tenantSettings","refreshSettings","validateSettings","settingsToValidate","errors","fieldSchema","value","expectedType","actualType","switchTenant","mode","redirectPath","newHostname","targetPath","urlParams","newUrl","useTenant","useTenantOptional","useTenantSettings","useSettings","useTenantInfo","retryTenant","AuthContext","AuthProvider","availableRoles","setAvailableRoles","rolesLoading","setRolesLoading","currentUser","setCurrentUser","isUserLoading","setIsUserLoading","userError","setUserError","userTenants","setUserTenants","hasTenantContext","setHasTenantContext","initRef","useRef","isLoadingAfterUrlTokens","setIsLoadingAfterUrlTokens","hasUrlTokens","manager","isRestoringSession","setIsRestoringSession","isAuthReady","authenticatedHttpService","service","authApiService","userApiService","roleApiService","userRole","role","userPermissions","isAuthenticated","loadUserDataRef","loadUserData","forceRefresh","userData","refreshUser","login","username","password","targetSlug","resolvedTenantId","targetSessionManager","loginResponse","shouldSwitch","hasTenant","autoSwitch","singleTenant","signup","email","phoneNumber","name","lastName","tenantId","signupTenantAdmin","tenantName","changePassword","currentPassword","newPassword","requestPasswordReset","confirmPasswordReset","sendMagicLink","frontendUrl","verifyMagicLink","verifyResponse","logout","setTokens","hasValidSession","clearSession","fetchRoles","roles","refreshRoles","hasPermission","permission","permissionString","permissions","targetTenant","t","tenants","internalHttpService","urlTokensCleanedUp","setUrlTokensCleanedUp","cancelled","useAuth","useAuthOptional","FeatureFlagApiService","flagKey","FeatureFlagContext","FeatureFlagProvider","appContext","tenantContext","featureFlags","setFeatureFlags","loading","setLoading","setError","initialLoadDone","setInitialLoadDone","featureFlagService","fetchFeatureFlags","refreshInterval","interval","isEnabled","flag","f","getFlag","getFlagState","refresh","isReady","useFeatureFlags","useFeatureFlagsOptional","SubscriptionApiService","subscriptionId","paymentData","SubscriptionContext","SubscriptionProvider","subscription","setSubscription","subscriptionService","fetchSubscription","features","isFeatureEnabled","featureKey","feature","getFeature","getFeatureValue","defaultValue","hasAllowedPlan","allowedPlans","useSubscription","useSubscriptionOptional","UserType","DEFAULT_ZONE_ROOTS","DEFAULT_ZONE_PRESETS","RoutingContext","RoutingProvider","zoneRoots","presets","useRouting","useRoutingOptional","DefaultFallback","jsxs","jsx","InsufficientPermissionsFallback","userType","minUserType","missingPermissions","Fragment","hasMinimumUserType","hierarchy","Protected","fallback","requiredPermissions","requireAllPermissions","hasAnyPermission","hasAllPermissions","requiredUserType","hasRequiredUserType","ProtectedRoute","redirectTo","useLocation","Navigate","DefaultTenantRequiredFallback","TenantRoute","isLoading","DefaultTenantDetectedFallback","LandingRoute","matchesUserType","currentType","required","checkAccessMode","condition","getAccessDeniedType","requirements","state","perms","p","getSmartRedirect","buildRedirectUrl","returnPath","currentPath","returnToParam","returnToStorage","returnUrl","separator","storeReturnUrl","ZoneRoute","preset","authMode","returnTo","onAccessDenied","loadingFallback","accessDeniedFallback","isAuthInitializing","routingConfig","presetConfig","accessDeniedType","redirectTarget","accessDeniedReason","finalRedirect","TenantZone","props","PublicZone","AuthenticatedZone","GuestZone","AdminZone","UserZone","OpenZone","TenantAuthenticatedZone","TenantOpenZone","TenantGuestZone","SubscriptionGuard","requiredFeature","flagName","FeatureFlag","EyeIcon","EyeOffIcon","defaultIcons","defaultCopy","defaultStyles","LoginForm","copy","styles","icons","onSuccess","onError","onForgotPassword","onSignupClick","onMagicLinkClick","showForgotPassword","showSignupLink","showMagicLinkOption","className","setUsername","setPassword","showPassword","setShowPassword","fieldErrors","setFieldErrors","mergedCopy","mergedStyles","mergedIcons","validateForm","handleSubmit","result","getInputStyle","field","getButtonStyle","prev","SignupForm","signupType","onLoginClick","showLoginLink","setName","setLastName","setEmail","setPhoneNumber","confirmPassword","setConfirmPassword","setTenantName","isFormValid","MagicLinkForm","showTraditionalLinks","verifyToken","verifying","setVerifying","success","setSuccess","showNameFields","setShowNameFields","handleVerifyMagicLink","finalFrontendUrl","LoadingIcon","SuccessIcon","ErrorIcon","MagicLinkVerify","onRetry","onBackToLogin","propToken","propEmail","propAppId","propTenantSlug","autoRedirectDelay","setState","getUrlParams","handleVerification","handleRetry","handleBackToLogin","renderContent","PasswordRecoveryForm","initialToken","onModeChange","setToken","setNewPassword","validateRequestForm","validateResetForm","handleRequestSubmit","handleResetSubmit","DefaultLoadingFallback","DefaultErrorFallback","retry","AppLoader","errorFallback","requireTenant","authContext","featureFlagsContext","subscriptionContext","isFeatureFlagsReady","isSubscriptionReady","shouldWaitForTenant","ErrorComponent","useAppLoaderState","TenantSelector","propTenants","propCurrentTenantId","propOnSelect","dropdownClassName","itemClassName","renderItem","placeholder","disabled","showCurrentTenant","auth","isOpen","setIsOpen","dropdownRef","currentTenantId","handleSelect","handleClickOutside","event","currentTenant","defaultRenderItem","isSelected","PermissionApiService","SubscriptionPlanApiService","HealthApiService","ApiMappers","dateString","date","meta","app","featureFlag","plan","searchParams","DEFAULT_RETURN_TO_PARAM","RETURN_TO_SESSION_KEY","RETURN_TO_LOCAL_KEY","useZoneNavigation","navigate","useNavigate","setSearchParams","useSearchParams","mergedZoneRoots","returnToUrl","clearReturnTo","newParams","setReturnTo","navigateToZone","zone","path"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/services/HttpService.ts","../src/services/AppApiService.ts","../src/providers/AppProvider.tsx","../src/errors/SessionErrors.ts","../src/services/SessionManager.ts","../src/services/AuthApiService.ts","../src/services/RoleApiService.ts","../src/services/UserApiService.ts","../src/services/TenantApiService.ts","../src/utils/tenantDetection.ts","../src/utils/crossDomainAuth.ts","../src/providers/TenantProvider.tsx","../src/providers/AuthProvider.tsx","../src/services/FeatureFlagApiService.ts","../src/providers/FeatureFlagProvider.tsx","../src/services/SubscriptionApiService.ts","../src/providers/SubscriptionProvider.tsx","../src/types/api.ts","../src/types/zoneRouting.ts","../src/providers/RoutingProvider.tsx","../src/components/Protected.tsx","../src/components/ProtectedRoute.tsx","../src/components/TenantRoute.tsx","../src/components/LandingRoute.tsx","../src/components/ZoneRoute.tsx","../src/components/SubscriptionGuard.tsx","../src/components/FeatureFlag.tsx","../src/components/LoginForm.tsx","../src/components/SignupForm.tsx","../src/components/MagicLinkForm.tsx","../src/components/MagicLinkVerify.tsx","../src/components/PasswordRecoveryForm.tsx","../src/components/AppLoader.tsx","../src/components/TenantSelector.tsx","../src/services/PermissionApiService.ts","../src/services/SubscriptionPlanApiService.ts","../src/services/HealthApiService.ts","../src/utils/mappers.ts","../src/hooks/useZoneNavigation.ts"],"sourcesContent":["import type { SessionManager } from './SessionManager';\n\nexport interface RequestOptions {\n headers?: Record<string, string>;\n timeout?: number;\n skipAuth?: boolean; // Skip automatic auth header injection\n}\n\nexport class HttpService {\n private baseUrl: string;\n private timeout: number;\n private sessionManager?: SessionManager;\n\n constructor(baseUrl: string, timeout = 10000) {\n this.baseUrl = baseUrl.replace(/\\/$/, ''); // Remove trailing slash\n this.timeout = timeout;\n }\n\n setSessionManager(sessionManager: SessionManager): void {\n this.sessionManager = sessionManager;\n }\n\n getBaseUrl(): string {\n return this.baseUrl;\n }\n\n private async executeRequest<T>(\n method: string,\n endpoint: string,\n data?: any,\n options?: RequestOptions\n ): Promise<T> {\n const url = `${this.baseUrl}${endpoint.startsWith('/') ? endpoint : `/${endpoint}`}`;\n const requestTimeout = options?.timeout || this.timeout;\n\n // Inject auth headers via SessionManager.getValidAccessToken()\n // SessionManager handles refresh, queue, retry, and error classification\n let requestHeaders: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...options?.headers,\n };\n\n if (!options?.skipAuth && this.sessionManager) {\n // SessionManager handles refresh, queue, retry, and error classification.\n // Throws SessionExpiredError | TokenRefreshTimeoutError | TokenRefreshError\n // which propagate to caller — they decide what to do.\n const accessToken = await this.sessionManager.getValidAccessToken();\n requestHeaders = { ...requestHeaders, Authorization: `Bearer ${accessToken}` };\n }\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), requestTimeout);\n\n try {\n const response = await fetch(url, {\n method,\n headers: requestHeaders,\n body: data ? JSON.stringify(data) : undefined,\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n\n // Handle empty responses\n const contentType = response.headers.get('content-type');\n if (!contentType || !contentType.includes('application/json')) {\n return {} as T;\n }\n\n return await response.json();\n } catch (error) {\n clearTimeout(timeoutId);\n\n if (error instanceof Error && error.name === 'AbortError') {\n throw new Error(`Request timeout after ${requestTimeout}ms`);\n }\n\n throw error;\n }\n }\n\n async get<T>(endpoint: string, options?: RequestOptions): Promise<T> {\n return this.executeRequest<T>('GET', endpoint, undefined, options);\n }\n\n async post<T>(endpoint: string, data: any, options?: RequestOptions): Promise<T> {\n return this.executeRequest<T>('POST', endpoint, data, options);\n }\n\n async put<T>(endpoint: string, data: any, options?: RequestOptions): Promise<T> {\n return this.executeRequest<T>('PUT', endpoint, data, options);\n }\n\n async delete<T>(endpoint: string, options?: RequestOptions): Promise<T> {\n return this.executeRequest<T>('DELETE', endpoint, undefined, options);\n }\n}\n","import { HttpService } from './HttpService';\nimport { SessionManager } from './SessionManager';\nimport type {\n ApiResponse,\n App,\n CreateAppRequest,\n PublicAppInfo,\n PaginationParams,\n} from '../types/api';\n\nexport class AppApiService {\n constructor(\n private httpService: HttpService,\n private sessionManager: SessionManager\n ) {}\n\n async createApp(request: CreateAppRequest): Promise<App> {\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const response = await this.httpService.post<ApiResponse<App>>('/apps/', request, {\n headers: authHeaders,\n });\n return response.data;\n }\n\n async getApps(params?: PaginationParams): Promise<{ apps: App[]; meta: any }> {\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const queryParams = new URLSearchParams();\n\n if (params?.page) queryParams.append('page', params.page.toString());\n if (params?.limit) queryParams.append('limit', params.limit.toString());\n if (params?.sortBy) queryParams.append('sortBy', params.sortBy);\n if (params?.sortOrder) queryParams.append('sortOrder', params.sortOrder);\n\n const url = `/apps/${queryParams.toString() ? `?${queryParams.toString()}` : ''}`;\n const response = await this.httpService.get<ApiResponse<App[]>>(url, {\n headers: authHeaders,\n });\n\n return {\n apps: response.data,\n meta: response.meta,\n };\n }\n\n async getAppById(id: string): Promise<App> {\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const response = await this.httpService.get<ApiResponse<App>>(`/apps/${id}`, {\n headers: authHeaders,\n });\n return response.data;\n }\n\n async updateApp(id: string, request: Partial<CreateAppRequest>): Promise<App> {\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const response = await this.httpService.put<ApiResponse<App>>(`/apps/${id}`, request, {\n headers: authHeaders,\n });\n return response.data;\n }\n\n async getPublicAppInfo(id: string): Promise<PublicAppInfo> {\n const response = await this.httpService.get<ApiResponse<PublicAppInfo>>(`/apps/${id}/public`);\n return response.data;\n }\n\n async setDefaultSubscriptionPlan(appId: string, planId: string): Promise<App> {\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const response = await this.httpService.put<ApiResponse<App>>(\n `/apps/${appId}/default-subscription-plan`,\n { planId },\n { headers: authHeaders }\n );\n return response.data;\n }\n\n async updateSettingsSchema(appId: string, schema: any, defaultSettings: any): Promise<App> {\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const response = await this.httpService.put<ApiResponse<App>>(\n `/apps/${appId}/settings-schema`,\n { schema, defaultSettings },\n { headers: authHeaders }\n );\n return response.data;\n }\n\n async exportConfig(appId: string): Promise<any> {\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const response = await this.httpService.get<ApiResponse<any>>(`/apps/${appId}/export-config`, {\n headers: authHeaders,\n });\n return response.data;\n }\n}\n","import {\n createContext,\n useContext,\n useMemo,\n ReactNode,\n useState,\n useEffect,\n useCallback,\n} from 'react';\nimport { HttpService } from '../services/HttpService';\nimport { AppApiService } from '../services/AppApiService';\nimport type { PublicAppInfo } from '../types/api';\n\n// RFC-003: Cache interface for app info\ninterface CachedAppInfo {\n data: PublicAppInfo;\n timestamp: number;\n appId: string;\n}\n\nexport interface AppConfig {\n baseUrl: string;\n appId: string;\n // RFC-003: Cache configuration\n cache?: {\n enabled?: boolean; // Default: true\n ttl?: number; // Time to live in milliseconds, default: 5 minutes\n storageKey?: string; // Default: 'app_cache_{appId}'\n };\n}\n\ninterface AppContextValue {\n appId: string;\n baseUrl: string;\n // App info with settings schema\n appInfo: PublicAppInfo | null;\n isAppLoading: boolean;\n appError: Error | null;\n retryApp: () => void;\n}\n\nconst AppContext = createContext<AppContextValue | null>(null);\n\ninterface AppProviderProps {\n config: AppConfig;\n children: ReactNode;\n}\n\nexport function AppProvider({ config, children }: AppProviderProps) {\n // RFC-003: Cache configuration with defaults\n const cacheConfig = useMemo(\n () => ({\n enabled: config.cache?.enabled ?? true,\n ttl: config.cache?.ttl ?? 5 * 60 * 1000, // 5 minutes default\n storageKey: config.cache?.storageKey ?? `app_cache_${config.appId}`,\n }),\n [config.cache, config.appId]\n );\n\n // RFC-003: Try to load from cache on initialization\n const [appInfo, setAppInfo] = useState<PublicAppInfo | null>(() => {\n if (!cacheConfig.enabled) return null;\n\n try {\n const cached = localStorage.getItem(cacheConfig.storageKey);\n if (!cached) return null;\n\n const parsed: CachedAppInfo = JSON.parse(cached);\n const now = Date.now();\n const age = now - parsed.timestamp;\n\n // Check if cache is still valid\n if (age < cacheConfig.ttl && parsed.appId === config.appId) {\n return parsed.data;\n }\n\n // Cache expired\n localStorage.removeItem(cacheConfig.storageKey);\n return null;\n } catch {\n return null;\n }\n });\n\n const [isAppLoading, setIsAppLoading] = useState(!appInfo); // Don't load if we have cache\n const [appError, setAppError] = useState<Error | null>(null);\n\n const contextValue = useMemo(() => {\n // Retry function for app loading\n const retryApp = () => {\n loadApp();\n };\n\n return {\n appId: config.appId,\n baseUrl: config.baseUrl,\n // App info\n appInfo,\n isAppLoading,\n appError,\n retryApp,\n };\n }, [config, appInfo, isAppLoading, appError]);\n\n // RFC-003: Load app info with caching\n const loadApp = useCallback(\n async (bypassCache = false) => {\n // Check cache first (unless bypassing)\n if (!bypassCache && cacheConfig.enabled && appInfo) {\n return; // Already have valid cached data\n }\n\n try {\n setIsAppLoading(true);\n setAppError(null);\n\n const httpService = new HttpService(config.baseUrl);\n const appApi = new AppApiService(httpService, {} as any); // SessionManager not needed for public endpoint\n const appData = await appApi.getPublicAppInfo(config.appId);\n setAppInfo(appData);\n\n // RFC-003: Save to cache\n if (cacheConfig.enabled) {\n try {\n const cacheData: CachedAppInfo = {\n data: appData,\n timestamp: Date.now(),\n appId: config.appId,\n };\n localStorage.setItem(cacheConfig.storageKey, JSON.stringify(cacheData));\n } catch (error) {\n if (process.env.NODE_ENV === 'development') {\n console.warn('[AppProvider] Failed to cache app info:', error);\n }\n }\n }\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to load app information');\n setAppError(error);\n setAppInfo(null);\n } finally {\n setIsAppLoading(false);\n }\n },\n [config.baseUrl, config.appId, cacheConfig, appInfo]\n );\n\n // RFC-003: Background refresh for stale-while-revalidate\n const backgroundRefresh = useCallback(async () => {\n if (!cacheConfig.enabled || !appInfo) return;\n\n try {\n const cached = localStorage.getItem(cacheConfig.storageKey);\n if (!cached) return;\n\n const parsed: CachedAppInfo = JSON.parse(cached);\n const age = Date.now() - parsed.timestamp;\n\n // If cache is more than 50% expired, refresh in background\n if (age > cacheConfig.ttl * 0.5) {\n const httpService = new HttpService(config.baseUrl);\n const appApi = new AppApiService(httpService, {} as any);\n const appData = await appApi.getPublicAppInfo(config.appId);\n\n setAppInfo(appData);\n\n const cacheData: CachedAppInfo = {\n data: appData,\n timestamp: Date.now(),\n appId: config.appId,\n };\n localStorage.setItem(cacheConfig.storageKey, JSON.stringify(cacheData));\n }\n } catch (error) {\n if (process.env.NODE_ENV === 'development') {\n console.warn('[AppProvider] Background app refresh failed:', error);\n }\n // Don't update error state - keep showing cached data\n }\n }, [config, cacheConfig, appInfo]);\n\n // RFC-003: Load app info on mount or do background refresh\n useEffect(() => {\n if (!appInfo) {\n loadApp();\n } else {\n // We have cached data, do background refresh\n backgroundRefresh();\n }\n }, []); // Only run on mount\n\n // No longer blocks children - loading state is exposed via context\n // Use AppLoader component to block until ready\n return <AppContext.Provider value={contextValue}>{children}</AppContext.Provider>;\n}\n\nexport function useApp(): AppContextValue {\n const context = useContext(AppContext);\n if (!context) {\n throw new Error('useApp must be used within an AppProvider');\n }\n return context;\n}\n\n/**\n * Optional hook that returns AppContext if available, null otherwise.\n */\nexport function useAppOptional(): AppContextValue | null {\n return useContext(AppContext);\n}\n\n// Backward compatibility\nexport const useApi = useApp;\n","/**\n * Session error classes for deterministic token refresh handling.\n *\n * These errors allow consumers to distinguish between:\n * - Fatal session errors (refresh token invalid → must logout)\n * - Timeout errors (queue wait exceeded → can retry)\n * - Transient refresh errors (network issues → system keeps retrying)\n */\n\nexport type SessionExpiredReason = 'token_expired' | 'token_invalid' | 'user_inactive';\n\n/**\n * Thrown when the refresh token is definitively invalid and the session must end.\n * Consumers should redirect to login when catching this error.\n */\nexport class SessionExpiredError extends Error {\n public readonly reason: SessionExpiredReason;\n\n constructor(reason: SessionExpiredReason, message?: string) {\n const defaultMessages: Record<SessionExpiredReason, string> = {\n token_expired: 'Refresh token has expired',\n token_invalid: 'Refresh token is invalid',\n user_inactive: 'User account is inactive',\n };\n super(message || defaultMessages[reason]);\n this.name = 'SessionExpiredError';\n this.reason = reason;\n }\n}\n\n/**\n * Thrown when a queued request exceeds its timeout waiting for a token refresh.\n * The refresh may still be in progress — the caller can retry.\n */\nexport class TokenRefreshTimeoutError extends Error {\n public readonly timeoutMs: number;\n\n constructor(timeoutMs: number) {\n super(`Token refresh timed out after ${timeoutMs}ms`);\n this.name = 'TokenRefreshTimeoutError';\n this.timeoutMs = timeoutMs;\n }\n}\n\n/**\n * Thrown when token refresh fails after all retries due to transient errors.\n * The proactive timer will keep retrying in the background.\n */\nexport class TokenRefreshError extends Error {\n public readonly attempts: number;\n public readonly lastError?: Error;\n\n constructor(attempts: number, lastError?: Error) {\n super(\n `Token refresh failed after ${attempts} attempts: ${lastError?.message || 'Unknown error'}`\n );\n this.name = 'TokenRefreshError';\n this.attempts = attempts;\n this.lastError = lastError;\n }\n}\n","import {\n SessionExpiredError,\n TokenRefreshTimeoutError,\n TokenRefreshError,\n} from '../errors/SessionErrors';\n\nexport interface TokenData {\n accessToken: string;\n refreshToken?: string;\n expiresAt?: number;\n expiresIn?: number;\n tokenType?: string;\n}\n\nexport interface JwtPayload {\n userId: string;\n email: string | null;\n phoneNumber: string | null;\n userType: string;\n role: string | null;\n tenantId: string | null;\n appId: string | null;\n iat?: number;\n exp?: number;\n}\n\nexport interface TokenStorage {\n get(): any;\n set(data: any): void;\n clear(): void;\n}\n\nexport interface SessionConfig {\n storageKey?: string;\n tenantSlug?: string | null;\n autoRefresh?: boolean;\n refreshThreshold?: number;\n /** @deprecated Use onSessionExpired instead */\n onRefreshFailed?: () => void;\n onSessionExpired?: (error: SessionExpiredError) => void;\n tokenStorage?: TokenStorage;\n baseUrl?: string;\n // New: deterministic refresh config\n proactiveRefreshMargin?: number; // ms before expiry to trigger proactive refresh (default: 60000)\n refreshQueueTimeout?: number; // ms before queued requests timeout (default: 10000)\n maxRefreshRetries?: number; // max retries per refresh attempt (default: 3)\n retryBackoffBase?: number; // base ms for exponential backoff (default: 1000)\n}\n\ninterface QueueEntry {\n resolve: (token: string) => void;\n reject: (error: Error) => void;\n timeoutId: ReturnType<typeof setTimeout>;\n}\n\nexport class SessionManager {\n // --- Singleton registry (keyed by storageKey) ---\n private static instances = new Map<string, SessionManager>();\n\n /**\n * Get or create a SessionManager instance for the given config.\n * Returns the same instance when called with the same storageKey/tenantSlug.\n * Mutable config (callbacks, baseUrl) is updated on the existing instance.\n */\n static getInstance(config: SessionConfig = {}): SessionManager {\n const key = SessionManager.resolveStorageKey(config);\n const existing = SessionManager.instances.get(key);\n if (existing) {\n existing.updateConfig(config);\n return existing;\n }\n const instance = new SessionManager(config);\n SessionManager.instances.set(key, instance);\n return instance;\n }\n\n /** Reset all singleton instances. For testing only. */\n static resetAllInstances(): void {\n for (const instance of SessionManager.instances.values()) {\n instance.destroy();\n }\n SessionManager.instances.clear();\n }\n\n private static resolveStorageKey(config: SessionConfig): string {\n if (config.storageKey) return config.storageKey;\n if (config.tenantSlug !== undefined) {\n return config.tenantSlug ? `auth_tokens_${config.tenantSlug}` : 'auth_tokens';\n }\n return 'auth_tokens';\n }\n\n private storageKey: string;\n private autoRefresh: boolean;\n private refreshThreshold: number;\n private baseUrl: string;\n private onRefreshFailed?: () => void;\n private onSessionExpired?: (error: SessionExpiredError) => void;\n private tokenStorage: TokenStorage;\n\n // New config\n private proactiveRefreshMargin: number;\n private refreshQueueTimeout: number;\n private maxRefreshRetries: number;\n private retryBackoffBase: number;\n\n // Refresh state\n private refreshPromise: Promise<void> | null = null;\n private refreshQueue: QueueEntry[] = [];\n private proactiveTimerId: ReturnType<typeof setTimeout> | null = null;\n private backgroundRetryTimerId: ReturnType<typeof setTimeout> | null = null;\n private isDestroyed = false;\n private sessionGeneration = 0;\n\n constructor(config: SessionConfig = {}) {\n if (config.tenantSlug !== undefined) {\n this.storageKey = config.tenantSlug ? `auth_tokens_${config.tenantSlug}` : 'auth_tokens';\n } else {\n this.storageKey = config.storageKey || 'auth_tokens';\n }\n\n this.autoRefresh = config.autoRefresh ?? true;\n this.refreshThreshold = config.refreshThreshold || 300000; // 5 minutes\n this.onRefreshFailed = config.onRefreshFailed;\n this.onSessionExpired = config.onSessionExpired;\n this.baseUrl = config.baseUrl || '';\n\n // New config with defaults\n this.proactiveRefreshMargin = config.proactiveRefreshMargin ?? 60000; // 1 minute\n this.refreshQueueTimeout = config.refreshQueueTimeout ?? 10000; // 10 seconds\n this.maxRefreshRetries = config.maxRefreshRetries ?? 3;\n this.retryBackoffBase = config.retryBackoffBase ?? 1000; // 1 second\n\n this.tokenStorage = config.tokenStorage || this.createTokenStorage(this.storageKey);\n\n // Schedule proactive refresh if we already have tokens\n this.scheduleProactiveRefresh();\n }\n\n /** Update mutable config (callbacks, baseUrl) on an existing instance. */\n private updateConfig(config: SessionConfig): void {\n if (config.onSessionExpired !== undefined) this.onSessionExpired = config.onSessionExpired;\n if (config.onRefreshFailed !== undefined) this.onRefreshFailed = config.onRefreshFailed;\n if (config.baseUrl) this.baseUrl = config.baseUrl;\n }\n\n // --- Storage helpers ---\n\n private createTokenStorage(storageKey: string): TokenStorage {\n return {\n get: () => {\n try {\n const stored = localStorage.getItem(storageKey);\n return stored ? JSON.parse(stored) : null;\n } catch {\n return null;\n }\n },\n set: (data: any) => {\n try {\n localStorage.setItem(storageKey, JSON.stringify(data));\n } catch {\n // Handle storage errors silently\n }\n },\n clear: () => {\n try {\n localStorage.removeItem(storageKey);\n } catch {\n // Handle storage errors silently\n }\n },\n };\n }\n\n // --- Token CRUD ---\n\n /**\n * Extract the `exp` claim from a JWT and convert to milliseconds.\n * Returns undefined if the token cannot be decoded or has no exp.\n */\n private static extractJwtExpiry(accessToken: string): number | undefined {\n try {\n const parts = accessToken.split('.');\n if (parts.length !== 3) return undefined;\n const payload = JSON.parse(atob(parts[1].replace(/-/g, '+').replace(/_/g, '/')));\n if (typeof payload.exp === 'number') {\n return payload.exp * 1000; // JWT exp is in seconds\n }\n return undefined;\n } catch {\n return undefined;\n }\n }\n\n setTokens(tokens: TokenData): void {\n const expiresAt =\n tokens.expiresAt ||\n (tokens.expiresIn ? Date.now() + tokens.expiresIn * 1000 : undefined) ||\n SessionManager.extractJwtExpiry(tokens.accessToken);\n\n const tokenData: TokenData = {\n ...tokens,\n expiresAt,\n };\n\n // Merge with existing storage to preserve non-token data (e.g. user)\n const currentData = this.tokenStorage.get() || {};\n this.tokenStorage.set({ ...currentData, ...tokenData });\n\n // Reschedule proactive refresh with new expiry\n this.scheduleProactiveRefresh();\n }\n\n getTokens(): TokenData | null {\n const { accessToken, refreshToken, expiresAt, expiresIn, tokenType } =\n this.tokenStorage.get() || {};\n\n if (!accessToken) {\n return null;\n }\n\n // Fallback: derive expiresAt from JWT exp claim when not stored\n const resolvedExpiresAt = expiresAt || SessionManager.extractJwtExpiry(accessToken);\n\n return {\n accessToken,\n refreshToken,\n expiresAt: resolvedExpiresAt,\n expiresIn,\n tokenType,\n };\n }\n\n clearTokens(): void {\n this.tokenStorage.clear();\n }\n\n isTokenExpired(token?: TokenData): boolean {\n const tokens = token || this.getTokens();\n if (!tokens?.expiresAt) return false;\n return Date.now() >= tokens.expiresAt;\n }\n\n shouldRefreshToken(token?: TokenData): boolean {\n const tokens = token || this.getTokens();\n if (!tokens?.expiresAt || !this.autoRefresh) return false;\n return Date.now() >= tokens.expiresAt - this.refreshThreshold;\n }\n\n getAccessToken(): string | null {\n const tokens = this.getTokens();\n return tokens?.accessToken || null;\n }\n\n // --- Proactive refresh timer ---\n\n private scheduleProactiveRefresh(): void {\n this.cancelProactiveTimer();\n if (!this.autoRefresh || this.isDestroyed) return;\n\n const tokens = this.getTokens();\n if (!tokens?.expiresAt || !tokens.refreshToken) return;\n\n const refreshAt = tokens.expiresAt - this.proactiveRefreshMargin;\n const delay = refreshAt - Date.now();\n\n if (delay <= 0) {\n // Already past the proactive refresh point — refresh now\n this.backgroundRefresh();\n return;\n }\n\n this.proactiveTimerId = setTimeout(() => {\n this.backgroundRefresh();\n }, delay);\n }\n\n private cancelProactiveTimer(): void {\n if (this.proactiveTimerId !== null) {\n clearTimeout(this.proactiveTimerId);\n this.proactiveTimerId = null;\n }\n if (this.backgroundRetryTimerId !== null) {\n clearTimeout(this.backgroundRetryTimerId);\n this.backgroundRetryTimerId = null;\n }\n }\n\n private backgroundRefresh(): void {\n if (this.isDestroyed) return;\n\n const tokens = this.getTokens();\n if (!tokens?.refreshToken) return;\n\n // If a refresh is already in progress (e.g. from getValidAccessToken), skip\n if (this.refreshPromise) return;\n\n const gen = this.sessionGeneration;\n\n // Use the shared refresh mechanism so refreshPromise is set.\n // This ensures concurrent getValidAccessToken() calls queue behind\n // this refresh instead of starting a duplicate one.\n this.startRefreshAndResolveQueue(tokens.refreshToken)\n .then(() => {\n // Success — scheduleProactiveRefresh is called by setTokens()\n })\n .catch(error => {\n if (error instanceof SessionExpiredError) {\n // Fatal — already handled by startRefreshAndResolveQueue\n } else if (this.sessionGeneration === gen) {\n // Transient — schedule background retry in 30s (only if session wasn't cleared)\n if (process.env.NODE_ENV === 'development') {\n console.warn(\n '[SessionManager] Background refresh failed, retrying in 30s:',\n error.message\n );\n }\n this.backgroundRetryTimerId = setTimeout(() => {\n this.backgroundRefresh();\n }, 30000);\n }\n });\n }\n\n /**\n * Wait for any in-progress token refresh to settle.\n * Returns true if refresh succeeded, false if it failed or no refresh was pending.\n */\n async waitForPendingRefresh(): Promise<boolean> {\n if (!this.refreshPromise) return false;\n try {\n await this.refreshPromise;\n return true;\n } catch {\n return false;\n }\n }\n\n // --- Core: getValidAccessToken with queue + timeout ---\n\n /**\n * Get a valid access token. If the token needs refresh, handles the refresh\n * with queuing, timeout, and retry logic.\n *\n * @throws {SessionExpiredError} if refresh token is invalid/expired → caller should logout\n * @throws {TokenRefreshTimeoutError} if queue wait exceeds timeout → caller can retry\n * @throws {TokenRefreshError} if refresh fails after all retries\n */\n async getValidAccessToken(): Promise<string> {\n const tokens = this.getTokens();\n\n // No tokens at all\n if (!tokens?.accessToken) {\n const error = new SessionExpiredError('token_invalid', 'No tokens available');\n this.handleSessionExpired(error);\n throw error;\n }\n\n // Token is valid and not near expiry — return immediately\n if (!this.shouldRefreshToken(tokens) && !this.isTokenExpired(tokens)) {\n return tokens.accessToken;\n }\n\n // Token needs refresh — no refresh token available\n if (!tokens.refreshToken) {\n const error = new SessionExpiredError('token_invalid', 'No refresh token available');\n this.handleSessionExpired(error);\n throw error;\n }\n\n // If refresh is already in progress, queue with timeout\n if (this.refreshPromise) {\n return this.enqueueForToken();\n }\n\n // Start the refresh process\n return this.startRefreshAndResolveQueue(tokens.refreshToken);\n }\n\n /**\n * Backward-compatible getAuthHeaders — now delegates to getValidAccessToken.\n */\n async getAuthHeaders(): Promise<Record<string, string>> {\n try {\n const accessToken = await this.getValidAccessToken();\n return { Authorization: `Bearer ${accessToken}` };\n } catch (error) {\n // Maintain backward compat: return empty headers instead of throwing\n // for code that expects the old behavior\n if (error instanceof SessionExpiredError) {\n // Legacy callback\n if (this.onRefreshFailed) {\n this.onRefreshFailed();\n }\n }\n return {};\n }\n }\n\n private enqueueForToken(): Promise<string> {\n return new Promise<string>((resolve, reject) => {\n const timeoutId = setTimeout(() => {\n // Remove this entry from the queue\n const idx = this.refreshQueue.findIndex(e => e.timeoutId === timeoutId);\n if (idx !== -1) {\n this.refreshQueue.splice(idx, 1);\n }\n reject(new TokenRefreshTimeoutError(this.refreshQueueTimeout));\n }, this.refreshQueueTimeout);\n\n this.refreshQueue.push({ resolve, reject, timeoutId });\n });\n }\n\n private async startRefreshAndResolveQueue(refreshToken: string): Promise<string> {\n // Create the shared promise\n this.refreshPromise = this.executeRefreshWithRetry(refreshToken);\n\n try {\n await this.refreshPromise;\n\n // Refresh successful — get new token\n const newTokens = this.getTokens();\n const newAccessToken = newTokens?.accessToken || '';\n\n // Resolve all queued requests\n this.resolveQueue(newAccessToken);\n\n return newAccessToken;\n } catch (error) {\n const err = error instanceof Error ? error : new Error('Token refresh failed');\n\n if (err instanceof SessionExpiredError) {\n // Fatal — reject all and trigger session expiry\n this.rejectQueue(err);\n this.handleSessionExpired(err);\n } else {\n // Transient — reject all queued with the error\n this.rejectQueue(err);\n }\n\n throw err;\n } finally {\n this.refreshPromise = null;\n }\n }\n\n private resolveQueue(accessToken: string): void {\n const queue = [...this.refreshQueue];\n this.refreshQueue = [];\n for (const entry of queue) {\n clearTimeout(entry.timeoutId);\n entry.resolve(accessToken);\n }\n }\n\n private rejectQueue(error: Error): void {\n const queue = [...this.refreshQueue];\n this.refreshQueue = [];\n for (const entry of queue) {\n clearTimeout(entry.timeoutId);\n entry.reject(error);\n }\n }\n\n // --- Refresh with retry + error classification ---\n\n private async executeRefreshWithRetry(refreshToken: string): Promise<void> {\n let lastError: Error | undefined;\n const gen = this.sessionGeneration;\n\n for (let attempt = 0; attempt <= this.maxRefreshRetries; attempt++) {\n // Bail out if session was cleared during retry (logout, session expired, etc.)\n if (this.sessionGeneration !== gen) {\n throw new SessionExpiredError('token_invalid', 'Session cleared during refresh');\n }\n\n try {\n await this.performTokenRefresh(refreshToken, gen);\n return; // Success\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n\n // Fatal errors — do not retry\n if (err instanceof SessionExpiredError) {\n throw err;\n }\n\n lastError = err;\n\n // Don't wait after the last attempt\n if (attempt < this.maxRefreshRetries) {\n const backoff = this.retryBackoffBase * Math.pow(2, attempt);\n await this.sleep(backoff);\n }\n }\n }\n\n // All retries exhausted\n throw new TokenRefreshError(this.maxRefreshRetries + 1, lastError);\n }\n\n /**\n * Single refresh attempt with error classification.\n * Throws SessionExpiredError for fatal errors (no retry).\n * Throws generic Error for transient errors (will be retried).\n */\n private async performTokenRefresh(refreshToken: string, gen: number): Promise<void> {\n if (!this.baseUrl) {\n throw new Error('Base URL not configured for token refresh');\n }\n\n const url = `${this.baseUrl}/auth/refresh`;\n\n let response: Response;\n try {\n response = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ refreshToken }),\n });\n } catch (networkError) {\n // Network error (e.g., TypeError: Failed to fetch) — transient\n throw networkError instanceof Error\n ? networkError\n : new Error('Network error during token refresh');\n }\n\n if (!response.ok) {\n // Try to extract error message from response body\n let errorMessage = '';\n try {\n const body = await response.json();\n errorMessage = (body.message || body.error || '').toLowerCase();\n } catch {\n errorMessage = response.statusText.toLowerCase();\n }\n\n // Classify the error based on status + message\n // All 401s are fatal — no retry\n if (response.status === 401) {\n if (errorMessage.includes('expired')) {\n throw new SessionExpiredError('token_expired');\n }\n if (errorMessage.includes('invalid')) {\n throw new SessionExpiredError('token_invalid');\n }\n // Unknown 401 — treat as fatal\n throw new SessionExpiredError('token_invalid', `Unauthorized: ${errorMessage}`);\n }\n\n if (response.status === 400) {\n if (errorMessage.includes('inactive')) {\n throw new SessionExpiredError('user_inactive');\n }\n // Expired/invalid tokens returned as 400 — also fatal\n if (errorMessage.includes('expired') || errorMessage.includes('invalid')) {\n throw new SessionExpiredError('token_invalid', errorMessage);\n }\n // Other 400s — transient (\"User not found\", \"Token refresh failed: ...\")\n throw new Error(`Token refresh failed (400): ${errorMessage}`);\n }\n\n // 5xx or other — transient\n throw new Error(`Token refresh failed: ${response.status} ${errorMessage}`);\n }\n\n // Session may have been cleared (logout) while the fetch was in-flight.\n // Do NOT write tokens back to storage if that happened.\n if (this.sessionGeneration !== gen) {\n throw new SessionExpiredError('token_invalid', 'Session cleared during refresh');\n }\n\n const refreshResponse = await response.json();\n\n this.setTokens({\n accessToken: refreshResponse.accessToken,\n refreshToken: refreshResponse.refreshToken || refreshToken,\n expiresIn: refreshResponse.expiresIn,\n });\n }\n\n // --- Session expiry handler ---\n\n private handleSessionExpired(error: SessionExpiredError): void {\n this.cancelProactiveTimer();\n this.clearSession();\n\n if (this.onSessionExpired) {\n this.onSessionExpired(error);\n } else if (this.onRefreshFailed) {\n // Legacy callback fallback\n this.onRefreshFailed();\n }\n }\n\n // --- User data ---\n\n setUser(user: any): void {\n const currentData = this.tokenStorage.get() || {};\n this.tokenStorage.set({ ...currentData, user });\n }\n\n getUser(): any | null {\n const data = this.tokenStorage.get();\n return data?.user || null;\n }\n\n clearUser(): void {\n const currentData = this.tokenStorage.get() || {};\n delete currentData.user;\n this.tokenStorage.set(currentData);\n }\n\n // --- Session lifecycle ---\n\n clearSession(): void {\n this.sessionGeneration++;\n this.cancelProactiveTimer();\n // clearTokens removes the entire storage entry (tokens + user data)\n this.clearTokens();\n\n // Reject any pending queue entries\n const expiredError = new SessionExpiredError('token_invalid', 'Session cleared');\n this.rejectQueue(expiredError);\n }\n\n /**\n * Dispose of this SessionManager instance.\n * Cancels all timers and rejects pending queue entries.\n */\n destroy(): void {\n this.isDestroyed = true;\n // Remove from singleton registry\n SessionManager.instances.delete(this.storageKey);\n this.cancelProactiveTimer();\n const error = new SessionExpiredError('token_invalid', 'SessionManager destroyed');\n this.rejectQueue(error);\n }\n\n // --- JWT helpers ---\n\n /**\n * Decode JWT token and extract payload\n * Returns null if token is invalid or cannot be decoded\n */\n getTokenPayload(): JwtPayload | null {\n try {\n const tokens = this.getTokens();\n if (!tokens?.accessToken) return null;\n\n // JWT structure: header.payload.signature\n const parts = tokens.accessToken.split('.');\n if (parts.length !== 3) return null;\n\n // Decode base64 payload (second part)\n const payload = parts[1];\n const decodedPayload = atob(payload.replace(/-/g, '+').replace(/_/g, '/'));\n return JSON.parse(decodedPayload) as JwtPayload;\n } catch {\n // Silent fail - invalid token format\n return null;\n }\n }\n\n /**\n * Get userId from token (source of truth) or fallback to stored user\n */\n getUserId(): string | null {\n // Priority 1: Get from JWT token (source of truth)\n const payload = this.getTokenPayload();\n if (payload?.userId) return payload.userId;\n\n // Priority 2: Fallback to stored user data\n const user = this.getUser();\n return user?.id || null;\n }\n\n hasValidSession(): boolean {\n const tokens = this.getTokens();\n return tokens !== null && !this.isTokenExpired(tokens);\n }\n\n // --- Utility ---\n\n private sleep(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms));\n }\n}\n","import { HttpService } from './HttpService';\nimport type {\n LoginRequest,\n LoginResponse,\n SignupRequest,\n ChangePasswordRequest,\n RefreshTokenRequest,\n RefreshTokenResponse,\n MagicLinkRequest,\n MagicLinkResponse,\n VerifyMagicLinkRequest,\n VerifyMagicLinkResponse,\n ApiResponse,\n User,\n SwitchTenantRequest,\n SwitchTenantResponse,\n UserTenantMembership,\n} from '../types/api';\n\nexport class AuthApiService {\n constructor(private httpService: HttpService) {}\n\n // Public endpoints - no auth required\n async login(request: LoginRequest): Promise<LoginResponse> {\n const response = await this.httpService.post<LoginResponse>('/auth/login', request);\n return response;\n }\n\n async signup(request: SignupRequest): Promise<User> {\n const response = await this.httpService.post<User>('/auth/signup', request);\n return response;\n }\n\n async signupTenantAdmin(request: {\n email?: string;\n phoneNumber?: string;\n name: string;\n lastName?: string;\n password: string;\n tenantName: string;\n appId?: string;\n }): Promise<{ user: User; tenant: any }> {\n const response = await this.httpService.post<{ user: User; tenant: any }>(\n '/auth/signup/tenant-admin',\n request\n );\n return response;\n }\n\n async refreshToken(request: RefreshTokenRequest): Promise<RefreshTokenResponse> {\n const response = await this.httpService.post<RefreshTokenResponse>('/auth/refresh', request);\n return response;\n }\n\n async switchTenant(request: SwitchTenantRequest): Promise<SwitchTenantResponse> {\n const response = await this.httpService.post<SwitchTenantResponse>(\n '/auth/switch-tenant',\n request\n );\n return response;\n }\n\n async getUserTenants(authHeaders: Record<string, string>): Promise<UserTenantMembership[]> {\n const response = await this.httpService.get<UserTenantMembership[]>('/auth/tenants', {\n headers: authHeaders,\n });\n return response;\n }\n\n async requestPasswordReset(request: { email: string; tenantId: string }): Promise<void> {\n await this.httpService.post<void>('/auth/password-reset/request', request);\n }\n\n async sendMagicLink(request: MagicLinkRequest): Promise<MagicLinkResponse> {\n const response = await this.httpService.post<MagicLinkResponse>(\n '/auth/magic-link/send',\n request\n );\n return response;\n }\n\n async verifyMagicLink(request: VerifyMagicLinkRequest): Promise<VerifyMagicLinkResponse> {\n const response = await this.httpService.post<VerifyMagicLinkResponse>(\n '/auth/magic-link/verify',\n request\n );\n return response;\n }\n\n async confirmPasswordReset(request: { token: string; newPassword: string }): Promise<void> {\n await this.httpService.post<void>('/auth/password-reset/confirm', request);\n }\n\n // Protected endpoints - auth required\n async changePassword(\n request: ChangePasswordRequest,\n authHeaders: Record<string, string>\n ): Promise<void> {\n await this.httpService.post<ApiResponse<null>>('/auth/change-password', request, {\n headers: authHeaders,\n });\n }\n}\n","import { HttpService } from './HttpService';\nimport { SessionManager } from './SessionManager';\nimport type {\n Role,\n CreateRoleRequest,\n AssignRoleRequest,\n ApiResponse,\n PaginationParams,\n} from '../types/api';\n\nexport class RoleApiService {\n constructor(\n private httpService: HttpService,\n private sessionManager?: SessionManager\n ) {}\n\n async createRole(request: CreateRoleRequest): Promise<Role> {\n if (!this.sessionManager) {\n throw new Error('SessionManager is required for private endpoints');\n }\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const response = await this.httpService.post<ApiResponse<Role>>('/roles/', request, {\n headers: authHeaders,\n });\n return response.data;\n }\n\n async getRoleById(id: string): Promise<Role> {\n if (!this.sessionManager) {\n throw new Error('SessionManager is required for private endpoints');\n }\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const response = await this.httpService.get<ApiResponse<Role>>(`/roles/${id}`, {\n headers: authHeaders,\n });\n return response.data;\n }\n\n async updateRole(id: string, request: Partial<CreateRoleRequest>): Promise<Role> {\n if (!this.sessionManager) {\n throw new Error('SessionManager is required for private endpoints');\n }\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const response = await this.httpService.put<ApiResponse<Role>>(`/roles/${id}`, request, {\n headers: authHeaders,\n });\n return response.data;\n }\n\n async deleteRole(id: string): Promise<void> {\n if (!this.sessionManager) {\n throw new Error('SessionManager is required for private endpoints');\n }\n const authHeaders = await this.sessionManager.getAuthHeaders();\n await this.httpService.delete<void>(`/roles/${id}`, {\n headers: authHeaders,\n });\n }\n\n // Public endpoint - no auth required\n async getRolesByApp(\n appId: string,\n params?: PaginationParams\n ): Promise<{ roles: Role[]; meta: any }> {\n const queryParams = new URLSearchParams();\n\n if (params?.page) queryParams.append('page', params.page.toString());\n if (params?.limit) queryParams.append('limit', params.limit.toString());\n if (params?.sortBy) queryParams.append('sortBy', params.sortBy);\n if (params?.sortOrder) queryParams.append('sortOrder', params.sortOrder);\n\n const url = `/roles/app/${appId}${queryParams.toString() ? `?${queryParams.toString()}` : ''}`;\n const response = await this.httpService.get<ApiResponse<Role[]>>(url);\n\n return {\n roles: response.data,\n meta: response.meta,\n };\n }\n\n async assignRole(roleId: string, request: AssignRoleRequest): Promise<void> {\n if (!this.sessionManager) {\n throw new Error('SessionManager is required for private endpoints');\n }\n const authHeaders = await this.sessionManager.getAuthHeaders();\n await this.httpService.post<ApiResponse<null>>(`/roles/${roleId}/assign`, request, {\n headers: authHeaders,\n });\n }\n\n async revokeRole(roleId: string, request: AssignRoleRequest): Promise<void> {\n if (!this.sessionManager) {\n throw new Error('SessionManager is required for private endpoints');\n }\n const authHeaders = await this.sessionManager.getAuthHeaders();\n await this.httpService.post<ApiResponse<null>>(`/roles/${roleId}/revoke`, request, {\n headers: authHeaders,\n });\n }\n\n async getUserRoles(\n userId: string,\n params?: PaginationParams\n ): Promise<{ roles: Role[]; meta: any }> {\n if (!this.sessionManager) {\n throw new Error('SessionManager is required for private endpoints');\n }\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const queryParams = new URLSearchParams();\n\n if (params?.page) queryParams.append('page', params.page.toString());\n if (params?.limit) queryParams.append('limit', params.limit.toString());\n if (params?.sortBy) queryParams.append('sortBy', params.sortBy);\n if (params?.sortOrder) queryParams.append('sortOrder', params.sortOrder);\n\n const url = `/roles/user/${userId}${queryParams.toString() ? `?${queryParams.toString()}` : ''}`;\n const response = await this.httpService.get<ApiResponse<Role[]>>(url, {\n headers: authHeaders,\n });\n\n return {\n roles: response.data,\n meta: response.meta,\n };\n }\n}\n","import { HttpService } from './HttpService';\nimport { SessionManager } from './SessionManager';\nimport type { User, CreateUserRequest, ApiResponse, PaginationParams } from '../types/api';\n\nexport class UserApiService {\n constructor(\n private httpService: HttpService,\n private sessionManager: SessionManager\n ) {}\n\n async createUser(request: CreateUserRequest): Promise<User> {\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const response = await this.httpService.post<ApiResponse<User>>('/users/', request, {\n headers: authHeaders,\n });\n return response.data;\n }\n\n async getUsers(params?: PaginationParams): Promise<{ users: User[]; meta: any }> {\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const queryParams = new URLSearchParams();\n\n if (params?.page) queryParams.append('page', params.page.toString());\n if (params?.limit) queryParams.append('limit', params.limit.toString());\n if (params?.sortBy) queryParams.append('sortBy', params.sortBy);\n if (params?.sortOrder) queryParams.append('sortOrder', params.sortOrder);\n\n const url = `/users/${queryParams.toString() ? `?${queryParams.toString()}` : ''}`;\n const response = await this.httpService.get<ApiResponse<User[]>>(url, {\n headers: authHeaders,\n });\n\n return {\n users: response.data,\n meta: response.meta,\n };\n }\n\n async getUserById(id: string): Promise<User> {\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const response = await this.httpService.get<ApiResponse<User>>(`/users/${id}`, {\n headers: authHeaders,\n });\n return response.data;\n }\n\n async updateUser(id: string, request: Partial<CreateUserRequest>): Promise<User> {\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const response = await this.httpService.put<ApiResponse<User>>(`/users/${id}`, request, {\n headers: authHeaders,\n });\n return response.data;\n }\n\n async deleteUser(id: string): Promise<void> {\n const authHeaders = await this.sessionManager.getAuthHeaders();\n await this.httpService.delete<void>(`/users/${id}`, {\n headers: authHeaders,\n });\n }\n}\n","import { HttpService } from './HttpService';\nimport { SessionManager } from './SessionManager';\nimport type {\n Tenant,\n CreateTenantRequest,\n PublicTenantInfo,\n TenantSettings,\n UpdateTenantSettingsRequest,\n ApiResponse,\n PaginationParams,\n} from '../types/api';\n\nexport class TenantApiService {\n constructor(\n private httpService: HttpService,\n private appId: string,\n private sessionManager?: SessionManager\n ) {}\n\n async createTenant(request: CreateTenantRequest): Promise<Tenant> {\n if (!this.sessionManager) {\n throw new Error('SessionManager is required for private endpoints');\n }\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const response = await this.httpService.post<ApiResponse<Tenant>>('/tenants/', request, {\n headers: authHeaders,\n });\n return response.data;\n }\n\n async getTenants(params?: PaginationParams): Promise<{ tenants: Tenant[]; meta: any }> {\n if (!this.sessionManager) {\n throw new Error('SessionManager is required for private endpoints');\n }\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const queryParams = new URLSearchParams();\n\n if (params?.page) queryParams.append('page', params.page.toString());\n if (params?.limit) queryParams.append('limit', params.limit.toString());\n if (params?.sortBy) queryParams.append('sortBy', params.sortBy);\n if (params?.sortOrder) queryParams.append('sortOrder', params.sortOrder);\n\n const url = `/tenants/${queryParams.toString() ? `?${queryParams.toString()}` : ''}`;\n const response = await this.httpService.get<ApiResponse<Tenant[]>>(url, {\n headers: authHeaders,\n });\n\n return {\n tenants: response.data,\n meta: response.meta,\n };\n }\n\n async getTenantById(id: string): Promise<Tenant> {\n if (!this.sessionManager) {\n throw new Error('SessionManager is required for private endpoints');\n }\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const response = await this.httpService.get<ApiResponse<Tenant>>(`/tenants/${id}`, {\n headers: authHeaders,\n });\n return response.data;\n }\n\n async updateTenant(id: string, request: Partial<CreateTenantRequest>): Promise<Tenant> {\n if (!this.sessionManager) {\n throw new Error('SessionManager is required for private endpoints');\n }\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const response = await this.httpService.put<ApiResponse<Tenant>>(`/tenants/${id}`, request, {\n headers: authHeaders,\n });\n return response.data;\n }\n\n async adminUpdateTenant(id: string, request: Partial<CreateTenantRequest>): Promise<Tenant> {\n if (!this.sessionManager) {\n throw new Error('SessionManager is required for private endpoints');\n }\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const response = await this.httpService.put<ApiResponse<Tenant>>(\n `/tenants/${id}/admin-update`,\n request,\n {\n headers: authHeaders,\n }\n );\n return response.data;\n }\n\n // Public endpoint - no auth required\n async getPublicTenantInfo(slug: string): Promise<PublicTenantInfo> {\n const response = await this.httpService.get<ApiResponse<PublicTenantInfo>>(\n `/tenants/${this.appId}/${slug}/public`\n );\n return response.data;\n }\n\n // Settings endpoints\n async getTenantSettings(id: string): Promise<TenantSettings> {\n const response = await this.httpService.get<ApiResponse<TenantSettings>>(\n `/tenants/${id}/settings`\n );\n return response.data;\n }\n\n async updateTenantSettings(\n id: string,\n request: UpdateTenantSettingsRequest\n ): Promise<TenantSettings> {\n if (!this.sessionManager) {\n throw new Error('SessionManager is required for private endpoints');\n }\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const response = await this.httpService.put<ApiResponse<TenantSettings>>(\n `/tenants/${id}/settings`,\n request,\n {\n headers: authHeaders,\n }\n );\n return response.data;\n }\n}\n","/**\n * Tenant detection utilities\n * Extracted for testability\n */\n\nexport interface TenantDetectionConfig {\n tenantMode: 'subdomain' | 'selector' | 'fixed';\n baseDomain?: string;\n selectorParam?: string;\n fixedTenantSlug?: string;\n}\n\nexport interface LocationInfo {\n hostname: string;\n search: string;\n}\n\n/**\n * Detect tenant slug from subdomain\n */\nexport function detectSubdomainTenant(hostname: string, baseDomain?: string): string | null {\n // Skip localhost and IP addresses\n const isLocalhost =\n hostname === 'localhost' || hostname.startsWith('127.') || hostname.startsWith('192.168.');\n\n if (isLocalhost) {\n return null;\n }\n\n // If baseDomain is configured, use it to extract subdomain\n if (baseDomain) {\n const baseDomainLower = baseDomain.toLowerCase();\n const currentHost = hostname.toLowerCase();\n\n // Check if we're on the base domain (no subdomain)\n if (currentHost === baseDomainLower || currentHost === `www.${baseDomainLower}`) {\n return null;\n }\n\n // Check if hostname ends with baseDomain\n if (currentHost.endsWith(`.${baseDomainLower}`)) {\n const subdomain = currentHost.slice(0, -(baseDomainLower.length + 1));\n // Ignore www subdomain\n if (subdomain === 'www') {\n return null;\n }\n return subdomain;\n }\n\n // Hostname doesn't match baseDomain\n return null;\n }\n\n // Fallback: Extract subdomain assuming format subdomain.domain.tld\n const parts = hostname.split('.');\n if (parts.length >= 3 && parts[0] !== 'www') {\n return parts[0];\n }\n\n return null;\n}\n\n/**\n * Detect tenant slug from URL selector parameter\n */\nexport function detectSelectorTenant(\n search: string,\n selectorParam: string = 'tenant',\n localStorage?: Storage | null\n): string | null {\n const urlParams = new URLSearchParams(search);\n const urlTenant = urlParams.get(selectorParam);\n\n if (urlTenant) {\n // Save to localStorage when found in URL\n if (localStorage) {\n localStorage.setItem('tenant', urlTenant);\n }\n return urlTenant;\n }\n\n // Fallback to localStorage if not in URL\n if (localStorage) {\n return localStorage.getItem('tenant');\n }\n\n return null;\n}\n\n/**\n * Main tenant detection function\n */\nexport function detectTenantSlug(\n config: TenantDetectionConfig,\n location: LocationInfo,\n localStorage?: Storage | null\n): string | null {\n const { tenantMode, baseDomain, selectorParam, fixedTenantSlug } = config;\n\n if (tenantMode === 'fixed') {\n return fixedTenantSlug || null;\n }\n\n if (tenantMode === 'subdomain') {\n return detectSubdomainTenant(location.hostname, baseDomain);\n }\n\n if (tenantMode === 'selector') {\n return detectSelectorTenant(location.search, selectorParam, localStorage);\n }\n\n return null;\n}\n\n/**\n * Build the target hostname for tenant switching in subdomain mode\n * @param targetTenantSlug - The tenant slug to switch to\n * @param currentHostname - The current window.location.hostname\n * @param baseDomain - Optional configured base domain\n * @returns The new hostname or null if unable to determine\n */\nexport function buildTenantHostname(\n targetTenantSlug: string,\n currentHostname: string,\n baseDomain?: string\n): string | null {\n // If baseDomain is configured, use it directly (recommended)\n if (baseDomain) {\n return `${targetTenantSlug}.${baseDomain}`;\n }\n\n // Fallback: try to detect from current hostname\n const parts = currentHostname.split('.');\n\n if (parts.length === 2) {\n // Root domain (e.g., kommi.click) - ADD subdomain at the beginning\n // kommi.click → test-admin.kommi.click\n return `${targetTenantSlug}.${currentHostname}`;\n } else if (parts.length >= 3) {\n // Already has subdomain (e.g., old-tenant.kommi.click) - REPLACE first part\n // old-tenant.kommi.click → test-admin.kommi.click\n parts[0] = targetTenantSlug;\n return parts.join('.');\n }\n\n // Single-part hostname (e.g., localhost) - cannot determine\n return null;\n}\n","/**\n * Cross-domain authentication utilities\n * Used to transfer auth tokens between subdomains during tenant switching\n */\n\nexport interface AuthTokens {\n accessToken: string;\n refreshToken: string;\n expiresIn: number;\n}\n\nconst AUTH_TRANSFER_PARAM = '_auth';\n\n/**\n * Encode auth tokens for URL transfer\n * Uses base64 encoding for safe URL transport\n */\nexport function encodeAuthTokens(tokens: AuthTokens): string {\n const payload = JSON.stringify(tokens);\n // Use base64url encoding (URL-safe)\n return btoa(payload).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=/g, '');\n}\n\n/**\n * Decode auth tokens from URL parameter\n */\nexport function decodeAuthTokens(encoded: string): AuthTokens | null {\n try {\n // Restore base64 padding and characters\n let base64 = encoded.replace(/-/g, '+').replace(/_/g, '/');\n // Add padding if needed\n while (base64.length % 4) {\n base64 += '=';\n }\n const payload = atob(base64);\n const tokens = JSON.parse(payload);\n\n // Validate structure\n if (\n typeof tokens.accessToken === 'string' &&\n typeof tokens.refreshToken === 'string' &&\n typeof tokens.expiresIn === 'number'\n ) {\n return tokens;\n }\n return null;\n } catch {\n return null;\n }\n}\n\n/**\n * Extract auth tokens from current URL if present\n */\nexport function extractAuthTokensFromUrl(): AuthTokens | null {\n if (typeof window === 'undefined') {\n return null;\n }\n\n const urlParams = new URLSearchParams(window.location.search);\n const encoded = urlParams.get(AUTH_TRANSFER_PARAM);\n\n if (!encoded) return null;\n\n const decoded = decodeAuthTokens(encoded);\n return decoded;\n}\n\n/**\n * Remove auth tokens from URL without page reload\n */\nexport function clearAuthTokensFromUrl(): void {\n if (typeof window === 'undefined') return;\n\n const url = new URL(window.location.href);\n url.searchParams.delete(AUTH_TRANSFER_PARAM);\n\n // Update URL without reload\n window.history.replaceState({}, '', url.toString());\n}\n\n/**\n * Build URL with auth tokens for cross-subdomain redirect\n */\nexport function buildUrlWithAuthTokens(baseUrl: string, tokens: AuthTokens, path?: string): string {\n const url = new URL(path || '/', baseUrl);\n url.searchParams.set(AUTH_TRANSFER_PARAM, encodeAuthTokens(tokens));\n return url.toString();\n}\n\n/**\n * Append auth tokens to an existing URL\n */\nexport function appendAuthTokensToUrl(url: string, tokens: AuthTokens): string {\n const urlObj = new URL(url);\n urlObj.searchParams.set(AUTH_TRANSFER_PARAM, encodeAuthTokens(tokens));\n return urlObj.toString();\n}\n\nexport { AUTH_TRANSFER_PARAM };\n","import {\n createContext,\n useContext,\n useMemo,\n ReactNode,\n useState,\n useEffect,\n useCallback,\n} from 'react';\nimport { useApp } from './AppProvider';\nimport { HttpService } from '../services/HttpService';\nimport { TenantApiService } from '../services/TenantApiService';\nimport { detectTenantSlug as detectTenant, buildTenantHostname } from '../utils/tenantDetection';\nimport { encodeAuthTokens, type AuthTokens, AUTH_TRANSFER_PARAM } from '../utils/crossDomainAuth';\nimport type { TenantSettings, JSONSchema, PublicTenantInfo } from '../types/api';\n\n// RFC-003: Cache interface for tenant info\ninterface CachedTenantInfo {\n data: PublicTenantInfo;\n timestamp: number;\n tenantSlug: string;\n}\n\nexport interface TenantConfig {\n // Tenant configuration\n tenantMode?: 'subdomain' | 'selector' | 'fixed';\n fixedTenantSlug?: string; // Required when tenantMode is 'fixed' — always uses this slug\n baseDomain?: string; // Base domain for subdomain mode (e.g., 'kommi.click')\n selectorParam?: string; // Default: 'tenant', used when tenantMode is 'selector'\n // RFC-003: Cache configuration\n cache?: {\n enabled?: boolean; // Default: true\n ttl?: number; // Time to live in milliseconds, default: 5 minutes\n storageKey?: string; // Default: 'tenant_cache_{tenantSlug}'\n };\n // SSR support\n initialTenant?: PublicTenantInfo;\n}\n\ninterface TenantContextValue {\n // Tenant info\n tenant: PublicTenantInfo | null;\n tenantSlug: string | null;\n isTenantLoading: boolean;\n tenantError: Error | null;\n retryTenant: () => void;\n // Settings\n settings: TenantSettings | null;\n settingsSchema: JSONSchema | null;\n isSettingsLoading: boolean;\n settingsError: Error | null;\n // Actions\n refreshSettings: () => void;\n switchTenant: (\n tenantSlug: string,\n options?: { mode?: 'navigate' | 'reload'; tokens?: AuthTokens; redirectPath?: string }\n ) => void;\n // Validation\n validateSettings: (settings: TenantSettings) => { isValid: boolean; errors: string[] };\n}\n\nconst TenantContext = createContext<TenantContextValue | null>(null);\n\ninterface TenantProviderProps {\n config: TenantConfig;\n children: ReactNode;\n}\n\nexport function TenantProvider({ config, children }: TenantProviderProps) {\n const { baseUrl, appInfo, appId } = useApp();\n\n // Detect tenant slug from URL using extracted utility\n const detectTenantSlug = useCallback((): string | null => {\n if (typeof window === 'undefined') return null;\n\n return detectTenant(\n {\n tenantMode: config.tenantMode || 'selector',\n baseDomain: config.baseDomain,\n selectorParam: config.selectorParam,\n fixedTenantSlug: config.fixedTenantSlug,\n },\n {\n hostname: window.location.hostname,\n search: window.location.search,\n },\n window.localStorage\n );\n }, [config.tenantMode, config.baseDomain, config.selectorParam, config.fixedTenantSlug]);\n\n // Detect tenant slug on mount and on URL changes\n const [tenantSlug, setTenantSlug] = useState<string | null>(() => detectTenantSlug());\n\n // RFC-003: Cache configuration with defaults\n const cacheConfig = useMemo(\n () => ({\n enabled: config.cache?.enabled ?? true,\n ttl: config.cache?.ttl ?? 5 * 60 * 1000, // 5 minutes default\n storageKey: config.cache?.storageKey ?? `tenant_cache_${tenantSlug || 'default'}`,\n }),\n [config.cache, tenantSlug]\n );\n\n // RFC-003: Try to load from cache on initialization\n const [tenant, setTenant] = useState<PublicTenantInfo | null>(() => {\n if (config.initialTenant) return config.initialTenant;\n if (!cacheConfig.enabled || !tenantSlug) return null;\n\n try {\n const cached = localStorage.getItem(cacheConfig.storageKey);\n if (!cached) return null;\n\n const parsed: CachedTenantInfo = JSON.parse(cached);\n const now = Date.now();\n const age = now - parsed.timestamp;\n\n // Check if cache is still valid\n if (age < cacheConfig.ttl && parsed.tenantSlug === tenantSlug) {\n return parsed.data;\n }\n\n // Cache expired\n localStorage.removeItem(cacheConfig.storageKey);\n return null;\n } catch {\n return null;\n }\n });\n\n const [isTenantLoading, setIsTenantLoading] = useState(!tenant && !config.initialTenant);\n const [tenantError, setTenantError] = useState<Error | null>(null);\n\n // Settings state\n const [settings, setSettings] = useState<TenantSettings | null>(null);\n const [isSettingsLoading, setIsSettingsLoading] = useState(false);\n const [settingsError, setSettingsError] = useState<Error | null>(null);\n\n // Re-detect tenant slug when URL changes (skip in fixed mode — slug never changes)\n useEffect(() => {\n if (config.tenantMode === 'fixed') return;\n const detected = detectTenantSlug();\n setTenantSlug(detected);\n }, [detectTenantSlug, config.tenantMode]);\n\n // Get settings schema from app info\n const settingsSchema = appInfo?.settingsSchema || null;\n\n // RFC-003: Load tenant info with caching\n const loadTenant = useCallback(\n async (slug: string, bypassCache = false) => {\n // Check cache first (unless bypassing)\n if (!bypassCache && cacheConfig.enabled && tenant && tenant.domain === slug) {\n return; // Already have valid cached data\n }\n\n try {\n setIsTenantLoading(true);\n setTenantError(null);\n\n const httpService = new HttpService(baseUrl);\n const tenantApi = new TenantApiService(httpService, appId);\n const tenantInfo = await tenantApi.getPublicTenantInfo(slug);\n setTenant(tenantInfo);\n\n // RFC-003: Save to cache\n if (cacheConfig.enabled) {\n try {\n const cacheData: CachedTenantInfo = {\n data: tenantInfo,\n timestamp: Date.now(),\n tenantSlug: slug,\n };\n localStorage.setItem(cacheConfig.storageKey, JSON.stringify(cacheData));\n } catch (error) {\n if (process.env.NODE_ENV === 'development') {\n console.warn('[TenantProvider] Failed to cache tenant info:', error);\n }\n }\n }\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to load tenant information');\n setTenantError(error);\n setTenant(null);\n } finally {\n setIsTenantLoading(false);\n }\n },\n [baseUrl, appId, cacheConfig, tenant]\n );\n\n // RFC-003: Background refresh for stale-while-revalidate\n const backgroundRefresh = useCallback(async () => {\n if (!cacheConfig.enabled || !tenant || !tenantSlug) return;\n\n try {\n const cached = localStorage.getItem(cacheConfig.storageKey);\n if (!cached) return;\n\n const parsed: CachedTenantInfo = JSON.parse(cached);\n const age = Date.now() - parsed.timestamp;\n\n // If cache is more than 50% expired, refresh in background\n if (age > cacheConfig.ttl * 0.5) {\n const httpService = new HttpService(baseUrl);\n const tenantApi = new TenantApiService(httpService, appId);\n const tenantInfo = await tenantApi.getPublicTenantInfo(tenantSlug);\n\n setTenant(tenantInfo);\n\n const cacheData: CachedTenantInfo = {\n data: tenantInfo,\n timestamp: Date.now(),\n tenantSlug,\n };\n localStorage.setItem(cacheConfig.storageKey, JSON.stringify(cacheData));\n }\n } catch (error) {\n if (process.env.NODE_ENV === 'development') {\n console.warn('[TenantProvider] Background tenant refresh failed:', error);\n }\n // Don't update error state - keep showing cached data\n }\n }, [baseUrl, appId, cacheConfig, tenant, tenantSlug]);\n\n // Load tenant settings\n const loadSettings = useCallback(async () => {\n if (!tenant?.id) return;\n\n try {\n setIsSettingsLoading(true);\n setSettingsError(null);\n\n const httpService = new HttpService(baseUrl);\n const tenantApi = new TenantApiService(httpService, tenant.appId);\n const tenantSettings = await tenantApi.getTenantSettings(tenant.id);\n setSettings(tenantSettings);\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to load tenant settings');\n setSettingsError(error);\n setSettings(null);\n } finally {\n setIsSettingsLoading(false);\n }\n }, [baseUrl, tenant]);\n\n // Refresh settings\n const refreshSettings = useCallback(() => {\n loadSettings();\n }, [loadSettings]);\n\n // Validate settings against schema\n const validateSettings = useCallback(\n (settingsToValidate: TenantSettings) => {\n if (!settingsSchema) {\n return { isValid: true, errors: [] };\n }\n\n const errors: string[] = [];\n\n try {\n // If settingsSchema has properties, validate against them\n if (settingsSchema.properties) {\n Object.entries(settingsSchema.properties).forEach(([key, fieldSchema]) => {\n const value = settingsToValidate[key];\n\n // Check required fields\n if (settingsSchema.required?.includes(key) && (value === undefined || value === null)) {\n errors.push(`Field '${key}' is required`);\n return;\n }\n\n // Skip validation if value is not provided and not required\n if (value === undefined || value === null) return;\n\n // Type validation using JSONSchema\n if (fieldSchema.type) {\n const expectedType = fieldSchema.type;\n const actualType = typeof value;\n\n if (expectedType === 'string' && actualType !== 'string') {\n errors.push(`Field '${key}' must be a string`);\n } else if (\n (expectedType === 'number' || expectedType === 'integer') &&\n actualType !== 'number'\n ) {\n errors.push(`Field '${key}' must be a number`);\n } else if (expectedType === 'boolean' && actualType !== 'boolean') {\n errors.push(`Field '${key}' must be a boolean`);\n } else if (expectedType === 'array' && !Array.isArray(value)) {\n errors.push(`Field '${key}' must be an array`);\n }\n }\n\n // String length validation\n if (\n fieldSchema.minLength !== undefined &&\n typeof value === 'string' &&\n value.length < fieldSchema.minLength\n ) {\n errors.push(\n `Field '${key}' must be at least ${fieldSchema.minLength} characters long`\n );\n }\n if (\n fieldSchema.maxLength !== undefined &&\n typeof value === 'string' &&\n value.length > fieldSchema.maxLength\n ) {\n errors.push(\n `Field '${key}' must be no more than ${fieldSchema.maxLength} characters long`\n );\n }\n\n // Number range validation\n if (\n fieldSchema.minimum !== undefined &&\n typeof value === 'number' &&\n value < fieldSchema.minimum\n ) {\n errors.push(`Field '${key}' must be at least ${fieldSchema.minimum}`);\n }\n if (\n fieldSchema.maximum !== undefined &&\n typeof value === 'number' &&\n value > fieldSchema.maximum\n ) {\n errors.push(`Field '${key}' must be no more than ${fieldSchema.maximum}`);\n }\n\n // Pattern validation for strings\n if (fieldSchema.pattern && typeof value === 'string') {\n const regex = new RegExp(fieldSchema.pattern);\n if (!regex.test(value)) {\n errors.push(`Field '${key}' does not match the required pattern`);\n }\n }\n\n // Enum validation\n if (fieldSchema.enum && !fieldSchema.enum.includes(value)) {\n errors.push(`Field '${key}' must be one of: ${fieldSchema.enum.join(', ')}`);\n }\n });\n }\n\n return {\n isValid: errors.length === 0,\n errors,\n };\n } catch {\n return {\n isValid: false,\n errors: ['Invalid settings schema or validation error'],\n };\n }\n },\n [settingsSchema]\n );\n\n // RFC-003: Load tenant on mount or do background refresh\n useEffect(() => {\n if (!config.initialTenant && tenantSlug) {\n if (!tenant) {\n // No cached data, fetch from server\n loadTenant(tenantSlug);\n } else {\n // We have cached data, do background refresh\n backgroundRefresh();\n }\n } else if (!config.initialTenant && !tenantSlug) {\n // No tenant slug found - continue without tenant\n setTenant(null);\n setTenantError(null);\n setIsTenantLoading(false);\n }\n }, [config.initialTenant, tenantSlug, tenant, loadTenant, backgroundRefresh]);\n\n // Load settings when tenant changes\n useEffect(() => {\n if (tenant?.id) {\n loadSettings();\n } else {\n setSettings(null);\n setSettingsError(null);\n setIsSettingsLoading(false);\n }\n }, [tenant?.id, loadSettings]);\n\n // Switch tenant by updating URL and reloading page\n const switchTenant = useCallback(\n (\n targetTenantSlug: string,\n options?: { mode?: 'navigate' | 'reload'; tokens?: AuthTokens; redirectPath?: string }\n ) => {\n const { mode = 'reload', tokens, redirectPath } = options || {};\n const tenantMode = config.tenantMode || 'selector';\n\n // Fixed mode: switching tenants is not supported\n if (tenantMode === 'fixed') {\n if (process.env.NODE_ENV === 'development') {\n console.warn(\n '[TenantProvider] switchTenant is a no-op in fixed mode. Tenant is always:',\n config.fixedTenantSlug\n );\n }\n // Still navigate to redirectPath if provided\n if (redirectPath) {\n window.location.href = redirectPath;\n }\n return;\n }\n\n // Update localStorage first\n localStorage.setItem('tenant', targetTenantSlug);\n\n if (tenantMode === 'subdomain') {\n // Subdomain mode: redirect to new subdomain\n const currentHostname = window.location.hostname;\n const newHostname = buildTenantHostname(\n targetTenantSlug,\n currentHostname,\n config.baseDomain\n );\n\n if (!newHostname) {\n if (process.env.NODE_ENV === 'development') {\n console.warn(\n '[TenantProvider] Cannot switch subdomain, invalid hostname:',\n currentHostname\n );\n }\n return;\n }\n\n // Build the new URL\n const targetPath = redirectPath || window.location.pathname;\n const url = new URL(`${window.location.protocol}//${newHostname}${targetPath}`);\n\n // Copy existing search params (except auth transfer)\n const currentParams = new URLSearchParams(window.location.search);\n currentParams.forEach((value, key) => {\n if (key !== AUTH_TRANSFER_PARAM) {\n url.searchParams.set(key, value);\n }\n });\n\n // If tokens provided, encode and add to URL for cross-subdomain auth\n if (tokens) {\n url.searchParams.set(AUTH_TRANSFER_PARAM, encodeAuthTokens(tokens));\n }\n\n window.location.href = url.toString();\n } else if (tenantMode === 'selector') {\n // Selector mode: update URL parameter\n const targetPath = redirectPath || window.location.pathname;\n const urlParams = new URLSearchParams(window.location.search);\n urlParams.set(config.selectorParam || 'tenant', targetTenantSlug);\n\n // Remove existing auth transfer param if present\n urlParams.delete(AUTH_TRANSFER_PARAM);\n\n // If tokens provided, encode and add to URL (same as subdomain mode)\n if (tokens) {\n urlParams.set(AUTH_TRANSFER_PARAM, encodeAuthTokens(tokens));\n }\n\n if (mode === 'reload') {\n // Full page reload with new tenant\n const newUrl = `${targetPath}?${urlParams.toString()}${window.location.hash}`;\n window.location.href = newUrl;\n } else {\n // Navigate without reload (requires router integration)\n const newUrl = `${targetPath}?${urlParams.toString()}${window.location.hash}`;\n window.history.pushState({}, '', newUrl);\n // Update state to trigger re-render\n setTenantSlug(targetTenantSlug);\n // Trigger tenant reload\n loadTenant(targetTenantSlug);\n }\n }\n },\n [config.tenantMode, config.selectorParam, config.baseDomain, loadTenant]\n );\n\n const contextValue = useMemo(() => {\n // Retry function for tenant loading\n const retryTenant = () => {\n if (tenantSlug) {\n loadTenant(tenantSlug);\n }\n };\n\n return {\n // Tenant info\n tenant,\n tenantSlug,\n isTenantLoading,\n tenantError,\n retryTenant,\n // Settings\n settings,\n settingsSchema,\n isSettingsLoading,\n settingsError,\n // Actions\n refreshSettings,\n switchTenant,\n // Validation\n validateSettings,\n };\n }, [\n tenant,\n tenantSlug,\n isTenantLoading,\n tenantError,\n settings,\n settingsSchema,\n isSettingsLoading,\n settingsError,\n refreshSettings,\n switchTenant,\n validateSettings,\n ]);\n\n // No longer blocks children - loading state is exposed via context\n // Use AppLoader component to block until ready\n return <TenantContext.Provider value={contextValue}>{children}</TenantContext.Provider>;\n}\n\nexport function useTenant(): TenantContextValue {\n const context = useContext(TenantContext);\n if (!context) {\n throw new Error('useTenant must be used within a TenantProvider');\n }\n return context;\n}\n\n// Optional hook that returns null if not inside TenantProvider\nexport function useTenantOptional(): TenantContextValue | null {\n return useContext(TenantContext);\n}\n\n// Backward compatibility\nexport const useTenantSettings = useTenant;\n\n// Convenience hook for just the settings\nexport function useSettings() {\n const { settings, settingsSchema, isSettingsLoading, settingsError, validateSettings } =\n useTenant();\n return {\n settings,\n settingsSchema,\n isLoading: isSettingsLoading,\n error: settingsError,\n validateSettings,\n };\n}\n\n// Convenience hook for just tenant info\nexport function useTenantInfo() {\n const { tenant, tenantSlug, isTenantLoading, tenantError, retryTenant } = useTenant();\n return {\n tenant,\n tenantSlug,\n isLoading: isTenantLoading,\n error: tenantError,\n retry: retryTenant,\n };\n}\n","import { createContext, useContext, ReactNode, useMemo, useState, useEffect, useRef } from 'react';\nimport { SessionManager } from '../services/SessionManager';\nimport { AuthApiService } from '../services/AuthApiService';\nimport { RoleApiService } from '../services/RoleApiService';\nimport { UserApiService } from '../services/UserApiService';\nimport { TenantApiService } from '../services/TenantApiService';\nimport { HttpService } from '../services/HttpService';\nimport { useApp } from './AppProvider';\nimport { useTenant } from './TenantProvider';\nimport { extractAuthTokensFromUrl, clearAuthTokensFromUrl } from '../utils/crossDomainAuth';\nimport { SessionExpiredError } from '../errors/SessionErrors';\nimport type {\n Role,\n Permission,\n User,\n LoginResponse,\n VerifyMagicLinkResponse,\n MagicLinkResponse,\n UserTenantMembership,\n} from '../types/api';\nimport type {\n LoginParams,\n SignupParams,\n SignupTenantAdminParams,\n SendMagicLinkParams,\n VerifyMagicLinkParams,\n RequestPasswordResetParams,\n ConfirmPasswordResetParams,\n ChangePasswordParams,\n} from '../types/authParams';\n\nexport interface AuthConfig {\n /** @deprecated Use onSessionExpired instead */\n onRefreshFailed?: () => void;\n onSessionExpired?: (error: SessionExpiredError) => void;\n initialRoles?: Role[]; // For SSR injection\n // Session config\n refreshQueueTimeout?: number; // ms before queued requests timeout (default: 10000)\n proactiveRefreshMargin?: number; // ms before expiry to trigger proactive refresh (default: 60000)\n // Multi-tenant options (RFC-004)\n autoSwitchSingleTenant?: boolean; // Auto-switch if user has only one tenant (default: true)\n onTenantSelectionRequired?: (tenants: UserTenantMembership[]) => void; // Callback when user needs to select tenant\n}\n\nexport interface AuthContextValue {\n // RFC-003: Authentication state\n isAuthenticated: boolean;\n sessionManager: SessionManager;\n authenticatedHttpService: HttpService; // Authenticated HttpService for protected endpoints\n // Auth methods (RFC-002: Object parameters)\n login: (params: LoginParams) => Promise<LoginResponse>;\n signup: (params: SignupParams) => Promise<User>;\n signupTenantAdmin: (params: SignupTenantAdminParams) => Promise<{ user: User; tenant: any }>;\n // Magic Link methods\n sendMagicLink: (params: SendMagicLinkParams) => Promise<MagicLinkResponse>;\n verifyMagicLink: (params: VerifyMagicLinkParams) => Promise<VerifyMagicLinkResponse>;\n changePassword: (params: ChangePasswordParams) => Promise<void>;\n requestPasswordReset: (params: RequestPasswordResetParams) => Promise<void>;\n confirmPasswordReset: (params: ConfirmPasswordResetParams) => Promise<void>;\n refreshToken: () => Promise<void>;\n logout: () => void;\n // Session methods\n setTokens: (tokens: { accessToken: string; refreshToken: string; expiresIn: number }) => void;\n hasValidSession: () => boolean;\n clearSession: () => void;\n // User data\n currentUser: User | null;\n isUserLoading: boolean;\n userError: Error | null;\n loadUserData: (forceRefresh?: boolean) => Promise<void>;\n refreshUser: () => Promise<void>;\n // Initialization state (for cross-subdomain auth)\n isAuthInitializing: boolean;\n isAuthReady: boolean;\n // Role and Permission methods\n userRole: Role | null;\n userPermissions: string[];\n availableRoles: Role[];\n rolesLoading: boolean;\n hasPermission: (permission: string | Permission) => boolean;\n hasAnyPermission: (permissions: (string | Permission)[]) => boolean;\n hasAllPermissions: (permissions: (string | Permission)[]) => boolean;\n getUserPermissionStrings: () => string[];\n refreshRoles: () => Promise<void>;\n // RFC-004: Multi-tenant user membership\n userTenants: UserTenantMembership[];\n hasTenantContext: boolean;\n switchToTenant: (tenantId: string, options?: { redirectPath?: string }) => Promise<void>;\n refreshUserTenants: () => Promise<UserTenantMembership[]>;\n}\n\nconst AuthContext = createContext<AuthContextValue | null>(null);\n\ninterface AuthProviderProps {\n config?: AuthConfig;\n children: ReactNode;\n}\n\nexport function AuthProvider({ config = {}, children }: AuthProviderProps) {\n const { appId, baseUrl } = useApp();\n const { tenant, tenantSlug, switchTenant } = useTenant();\n const [availableRoles, setAvailableRoles] = useState<Role[]>(config.initialRoles || []);\n const [rolesLoading, setRolesLoading] = useState(!config.initialRoles);\n const [currentUser, setCurrentUser] = useState<User | null>(null);\n const [isUserLoading, setIsUserLoading] = useState(false);\n const [userError, setUserError] = useState<Error | null>(null);\n\n // RFC-004: Multi-tenant user membership state\n const [userTenants, setUserTenants] = useState<UserTenantMembership[]>(() => {\n // Try to load cached tenants from localStorage\n try {\n const cached = localStorage.getItem('userTenants');\n return cached ? JSON.parse(cached) : [];\n } catch {\n return [];\n }\n });\n const [hasTenantContext, setHasTenantContext] = useState<boolean>(false);\n\n // === SYNCHRONOUS INITIALIZATION ===\n // Process URL tokens and localStorage BEFORE first render completes\n // This ensures guards see valid session immediately\n const initRef = useRef<{\n done: boolean;\n urlTokens: ReturnType<typeof extractAuthTokensFromUrl>;\n }>({ done: false, urlTokens: null });\n\n // Extract URL tokens synchronously on first render\n if (!initRef.current.done) {\n initRef.current.done = true;\n initRef.current.urlTokens = extractAuthTokensFromUrl();\n // URL tokens found — will block isAuthReady until user is loaded\n }\n\n // Track if we're loading user after URL token consumption\n // CRITICAL: Initialize to TRUE if we have URL tokens, so isAuthReady stays false until user is loaded\n const [isLoadingAfterUrlTokens, setIsLoadingAfterUrlTokens] = useState(() => {\n const hasUrlTokens = initRef.current.urlTokens !== null;\n return hasUrlTokens;\n });\n\n // Create services with stable references — singleton per tenantSlug\n const sessionManager = useMemo(() => {\n const manager = SessionManager.getInstance({\n tenantSlug: tenantSlug,\n baseUrl: baseUrl,\n refreshQueueTimeout: config.refreshQueueTimeout,\n proactiveRefreshMargin: config.proactiveRefreshMargin,\n onSessionExpired: (error: SessionExpiredError) => {\n // Clear React auth state when session expires\n setCurrentUser(null);\n setUserError(null);\n setUserTenants([]);\n setHasTenantContext(false);\n try {\n localStorage.removeItem('userTenants');\n } catch {\n // Ignore localStorage errors\n }\n // Call user callbacks\n if (config.onSessionExpired) {\n config.onSessionExpired(error);\n } else if (config.onRefreshFailed) {\n config.onRefreshFailed();\n }\n },\n });\n\n // If we have URL tokens, save them immediately (sync)\n if (initRef.current.urlTokens) {\n manager.setTokens({\n accessToken: initRef.current.urlTokens.accessToken,\n refreshToken: initRef.current.urlTokens.refreshToken,\n expiresIn: initRef.current.urlTokens.expiresIn,\n });\n }\n\n return manager;\n }, [tenantSlug, baseUrl, config.refreshQueueTimeout, config.proactiveRefreshMargin]);\n\n // Track if we're restoring an existing session (tokens in localStorage but user not loaded yet)\n // CRITICAL: Initialize to TRUE if there's a valid session OR if tokens are expired but\n // refreshable (backgroundRefresh is pending). This keeps isAuthReady false until\n // the refresh attempt settles, preventing premature redirects to login.\n const [isRestoringSession, setIsRestoringSession] = useState(() => {\n if (initRef.current.urlTokens) return false; // URL tokens path handles its own blocking\n const tokens = sessionManager.getTokens();\n if (!tokens) return false;\n // Valid session (need to load user) OR expired but has refreshToken (refresh pending)\n return sessionManager.hasValidSession() || !!tokens.refreshToken;\n });\n\n // Auth is ready when:\n // 1. Initial token check is done AND\n // 2. If we had URL tokens, user data must be loaded (not loading) AND\n // 3. If we had an existing session, user data must be restored\n const isAuthReady = initRef.current.done && !isLoadingAfterUrlTokens && !isRestoringSession;\n\n const authenticatedHttpService = useMemo(() => {\n const service = new HttpService(baseUrl);\n service.setSessionManager(sessionManager);\n return service;\n }, [baseUrl, sessionManager]);\n\n const authApiService = useMemo(() => {\n return new AuthApiService(new HttpService(baseUrl));\n }, [baseUrl]);\n\n const userApiService = useMemo(() => {\n return new UserApiService(authenticatedHttpService, sessionManager);\n }, [authenticatedHttpService, sessionManager]);\n\n const roleApiService = useMemo(() => {\n return new RoleApiService(new HttpService(baseUrl));\n }, [baseUrl]);\n\n // Calculate derived values with useMemo to prevent recalculation\n const user = useMemo(() => {\n return currentUser || sessionManager.getUser();\n }, [currentUser, sessionManager]);\n\n const userRole = useMemo(() => {\n return user?.roleId ? availableRoles.find(role => role.id === user.roleId) || null : null;\n }, [user, availableRoles]);\n\n const userPermissions = useMemo(() => {\n const permissions = userRole?.permissions || [];\n // Permissions from API are already strings in 'resource.action' format\n return permissions;\n }, [userRole]);\n\n // RFC-003: Compute isAuthenticated from session state\n const isAuthenticated = useMemo(() => {\n return sessionManager.hasValidSession() && currentUser !== null;\n }, [sessionManager, currentUser]);\n\n // Stable ref so effects can call loadUserData without depending on contextValue\n const loadUserDataRef = useRef<(forceRefresh?: boolean) => Promise<void>>(async () => {});\n\n const contextValue = useMemo(() => {\n // Load user data from API with cache control\n const loadUserData = async (forceRefresh = false) => {\n try {\n // 1. Check if we have a valid session\n if (!sessionManager.hasValidSession()) {\n return;\n }\n\n // 2. Skip if we already have user data (unless forced)\n if (!forceRefresh && currentUser) {\n return;\n }\n\n // 3. Get userId from token (source of truth) or fallback to stored user\n const userId = sessionManager.getUserId();\n if (!userId) {\n if (process.env.NODE_ENV === 'development') {\n console.warn('[AuthProvider] No userId available in token or storage');\n }\n return;\n }\n\n setIsUserLoading(true);\n setUserError(null);\n\n const userData = await userApiService.getUserById(userId);\n setCurrentUser(userData);\n sessionManager.setUser(userData); // Update session with fresh data\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to load user data');\n setUserError(error);\n if (process.env.NODE_ENV === 'development') {\n console.error('[AuthProvider] Failed to load user data:', error);\n }\n } finally {\n setIsUserLoading(false);\n }\n };\n\n const refreshUser = async () => {\n await loadUserData();\n };\n\n // Auth methods (RFC-002: Object parameters + RFC-001: Auto-switch + RFC-004: Multi-tenant)\n const login = async (params: LoginParams): Promise<LoginResponse> => {\n const { username, password, tenantSlug: targetSlug, redirectPath } = params;\n\n // RFC-001: Get tenantId from slug if provided, otherwise use current context\n let resolvedTenantId = tenant?.id;\n let targetTenantSlug = tenantSlug;\n let targetSessionManager = sessionManager;\n\n if (targetSlug) {\n // Get tenant ID from public endpoint using slug\n const tenantApi = new TenantApiService(authenticatedHttpService, appId);\n const tenantInfo = await tenantApi.getPublicTenantInfo(targetSlug);\n resolvedTenantId = tenantInfo.id;\n targetTenantSlug = targetSlug;\n }\n\n const loginResponse = await authApiService.login({\n username,\n password,\n appId,\n tenantId: resolvedTenantId,\n });\n\n // Check if we need to switch\n const shouldSwitch = targetSlug && targetSlug !== tenantSlug;\n\n // Save tokens to the correct tenant\n\n if (shouldSwitch) {\n // If switching, create a new SessionManager for the target tenant\n targetSessionManager = new SessionManager({\n tenantSlug: targetTenantSlug,\n baseUrl: baseUrl,\n });\n }\n\n targetSessionManager.setTokens({\n accessToken: loginResponse.accessToken,\n refreshToken: loginResponse.refreshToken,\n expiresIn: loginResponse.expiresIn,\n });\n\n // Store user data if available in the response\n if (loginResponse.user) {\n targetSessionManager.setUser(loginResponse.user);\n setCurrentUser(loginResponse.user);\n\n // Load complete user data from API after login\n try {\n await loadUserData();\n } catch (error) {\n if (process.env.NODE_ENV === 'development') {\n console.warn('[AuthProvider] Failed to load complete user data after login:', error);\n }\n }\n }\n\n // RFC-004: Store user tenants from login response\n if (loginResponse.tenants && loginResponse.tenants.length > 0) {\n setUserTenants(loginResponse.tenants);\n // Cache in localStorage\n try {\n localStorage.setItem('userTenants', JSON.stringify(loginResponse.tenants));\n } catch {\n // Ignore localStorage errors\n }\n }\n\n // RFC-004: Determine if we have tenant context\n const hasTenant = loginResponse.user?.tenantId !== null;\n setHasTenantContext(hasTenant);\n\n // Build tokens object for redirect\n const tokens = {\n accessToken: loginResponse.accessToken,\n refreshToken: loginResponse.refreshToken,\n expiresIn: loginResponse.expiresIn,\n };\n\n // Handle navigation after login\n if (shouldSwitch && targetTenantSlug) {\n // Switching to different tenant - use switchTenant for cross-subdomain auth\n switchTenant(targetTenantSlug, { tokens, redirectPath });\n return loginResponse; // Code after this won't execute due to page reload\n }\n\n // Same tenant or no tenant switch - navigate to redirectPath if provided\n if (redirectPath && redirectPath !== window.location.pathname) {\n // Use switchTenant even for same tenant to handle redirect consistently\n switchTenant(targetTenantSlug || tenantSlug || '', { tokens, redirectPath });\n return loginResponse;\n }\n\n // RFC-004: Handle global login (no tenantId) - auto-switch or callback\n if (!hasTenant && loginResponse.tenants && loginResponse.tenants.length > 0) {\n const autoSwitch = params.autoSwitch !== false && config.autoSwitchSingleTenant !== false; // default true\n\n if (loginResponse.tenants.length === 1 && autoSwitch) {\n // Auto-switch to the only tenant\n const singleTenant = loginResponse.tenants[0];\n switchTenant(singleTenant.subdomain, { tokens, redirectPath });\n return loginResponse;\n } else if (loginResponse.tenants.length > 1 && config.onTenantSelectionRequired) {\n // Multiple tenants - trigger callback for tenant selection\n config.onTenantSelectionRequired(loginResponse.tenants);\n }\n }\n\n return loginResponse;\n };\n\n const signup = async (params: SignupParams): Promise<User> => {\n const { email, phoneNumber, name, password, lastName, tenantId } = params;\n\n if (!email && !phoneNumber) {\n throw new Error('Either email or phoneNumber is required');\n }\n if (!name || !password) {\n throw new Error('Name and password are required');\n }\n\n const resolvedTenantId = tenantId ?? tenant?.id;\n\n const signupResponse = await authApiService.signup({\n email,\n phoneNumber,\n name,\n password,\n tenantId: resolvedTenantId,\n lastName,\n appId,\n });\n return signupResponse;\n };\n\n const signupTenantAdmin = async (\n params: SignupTenantAdminParams\n ): Promise<{ user: User; tenant: any }> => {\n const { email, phoneNumber, name, password, tenantName, lastName } = params;\n\n if (!email && !phoneNumber) {\n throw new Error('Either email or phoneNumber is required');\n }\n if (!name || !password || !tenantName) {\n throw new Error('Name, password, and tenantName are required');\n }\n\n const signupResponse = await authApiService.signupTenantAdmin({\n email,\n phoneNumber,\n name,\n password,\n tenantName,\n appId,\n lastName,\n });\n return signupResponse;\n };\n\n const changePassword = async (params: ChangePasswordParams): Promise<void> => {\n const { currentPassword, newPassword } = params;\n const authHeaders = await sessionManager.getAuthHeaders();\n await authApiService.changePassword({ currentPassword, newPassword }, authHeaders);\n };\n\n const requestPasswordReset = async (params: RequestPasswordResetParams): Promise<void> => {\n const { email, tenantId } = params;\n const resolvedTenantId = tenantId ?? tenant?.id;\n\n if (!resolvedTenantId) {\n throw new Error('tenantId is required for password reset');\n }\n\n await authApiService.requestPasswordReset({ email, tenantId: resolvedTenantId });\n };\n\n const confirmPasswordReset = async (params: ConfirmPasswordResetParams): Promise<void> => {\n const { token, newPassword } = params;\n await authApiService.confirmPasswordReset({ token, newPassword });\n };\n\n // Magic Link methods\n const sendMagicLink = async (params: SendMagicLinkParams): Promise<MagicLinkResponse> => {\n const { email, frontendUrl, name, lastName, tenantId } = params;\n const resolvedTenantId = tenantId ?? tenant?.id;\n\n if (!resolvedTenantId) {\n throw new Error('tenantId is required for magic link authentication');\n }\n\n const response = await authApiService.sendMagicLink({\n email,\n tenantId: resolvedTenantId,\n frontendUrl,\n name,\n lastName,\n appId,\n });\n return response;\n };\n\n const verifyMagicLink = async (\n params: VerifyMagicLinkParams\n ): Promise<VerifyMagicLinkResponse> => {\n const { token, email, tenantSlug: targetSlug } = params;\n\n // RFC-001: Get tenantId from slug if provided, otherwise use current context\n let resolvedTenantId = tenant?.id;\n let targetTenantSlug = tenantSlug;\n let targetSessionManager = sessionManager;\n\n if (targetSlug) {\n // Get tenant ID from public endpoint using slug\n const tenantApi = new TenantApiService(authenticatedHttpService, appId);\n const tenantInfo = await tenantApi.getPublicTenantInfo(targetSlug);\n resolvedTenantId = tenantInfo.id;\n targetTenantSlug = targetSlug;\n }\n\n const verifyResponse = await authApiService.verifyMagicLink({\n token,\n email,\n appId,\n tenantId: resolvedTenantId,\n });\n\n // Check if we need to switch\n const shouldSwitch = targetSlug && targetSlug !== tenantSlug;\n\n // Save tokens to the correct tenant\n\n if (shouldSwitch) {\n // If switching, create a new SessionManager for the target tenant\n targetSessionManager = new SessionManager({\n tenantSlug: targetTenantSlug,\n baseUrl: baseUrl,\n });\n }\n targetSessionManager.setTokens({\n accessToken: verifyResponse.accessToken,\n refreshToken: verifyResponse.refreshToken,\n expiresIn: verifyResponse.expiresIn,\n });\n\n // Store user data\n if (verifyResponse.user) {\n targetSessionManager.setUser(verifyResponse.user);\n setCurrentUser(verifyResponse.user);\n\n // Load complete user data from API after magic link login\n try {\n await loadUserData();\n } catch (error) {\n if (process.env.NODE_ENV === 'development') {\n console.warn(\n '[AuthProvider] Failed to load complete user data after magic link:',\n error\n );\n }\n }\n }\n\n // Now perform the switch if needed\n if (shouldSwitch && targetTenantSlug && targetTenantSlug !== tenantSlug) {\n // Pass tokens for cross-subdomain auth\n switchTenant(targetTenantSlug, {\n tokens: {\n accessToken: verifyResponse.accessToken,\n refreshToken: verifyResponse.refreshToken,\n expiresIn: verifyResponse.expiresIn,\n },\n });\n // Code after this won't execute due to page reload\n }\n\n return verifyResponse;\n };\n\n const refreshToken = async () => {\n const tokens = sessionManager.getTokens();\n if (!tokens?.refreshToken) {\n throw new Error('No refresh token available');\n }\n\n const refreshResponse = await authApiService.refreshToken({\n refreshToken: tokens.refreshToken,\n });\n\n sessionManager.setTokens({\n accessToken: refreshResponse.accessToken,\n refreshToken: refreshResponse.refreshToken || tokens.refreshToken,\n expiresIn: refreshResponse.expiresIn,\n });\n };\n\n const logout = () => {\n sessionManager.clearSession();\n setCurrentUser(null);\n setUserError(null);\n // RFC-004: Clear multi-tenant state\n setUserTenants([]);\n setHasTenantContext(false);\n try {\n localStorage.removeItem('userTenants');\n } catch {\n // Ignore localStorage errors\n }\n };\n\n const setTokens = (tokens: {\n accessToken: string;\n refreshToken: string;\n expiresIn: number;\n }) => {\n sessionManager.setTokens(tokens);\n };\n\n const hasValidSession = () => {\n return sessionManager.hasValidSession();\n };\n\n const clearSession = () => {\n sessionManager.clearSession();\n setCurrentUser(null);\n setUserError(null);\n };\n\n // Role and Permission methods\n const fetchRoles = async () => {\n if (!appId) return;\n\n try {\n setRolesLoading(true);\n const { roles } = await roleApiService.getRolesByApp(appId);\n setAvailableRoles(roles);\n } catch (error) {\n if (process.env.NODE_ENV === 'development') {\n console.error('[AuthProvider] Failed to fetch roles:', error);\n }\n } finally {\n setRolesLoading(false);\n }\n };\n\n const refreshRoles = async () => {\n await fetchRoles();\n };\n\n // Helper functions for permission checks\n const hasPermission = (permission: string | Permission): boolean => {\n if (!userPermissions || userPermissions.length === 0) {\n return false;\n }\n\n if (typeof permission === 'string') {\n // userPermissions is now an array of strings in 'resource.action' format\n return userPermissions.includes(permission);\n }\n\n // For Permission objects, convert to string and check\n const permissionString = `${permission.resource}.${permission.action}`;\n return userPermissions.includes(permissionString);\n };\n\n const hasAnyPermission = (permissions: (string | Permission)[]): boolean => {\n return permissions.some(permission => hasPermission(permission));\n };\n\n const hasAllPermissions = (permissions: (string | Permission)[]): boolean => {\n return permissions.every(permission => hasPermission(permission));\n };\n\n // Utility function to get all user permissions in resource.action format\n const getUserPermissionStrings = (): string[] => {\n if (!userPermissions) return [];\n // userPermissions is already an array of strings\n return userPermissions;\n };\n\n // RFC-004: Switch to a different tenant without re-authentication\n const switchToTenant = async (\n tenantId: string,\n options?: { redirectPath?: string }\n ): Promise<void> => {\n const { redirectPath } = options || {};\n\n // Get refresh token from session\n const tokens = sessionManager.getTokens();\n if (!tokens?.refreshToken) {\n throw new Error('No refresh token available for tenant switch');\n }\n\n // Call switch-tenant endpoint\n const response = await authApiService.switchTenant({\n refreshToken: tokens.refreshToken,\n tenantId,\n });\n\n // Update tokens with tenant-scoped token\n sessionManager.setTokens({\n accessToken: response.accessToken,\n refreshToken: tokens.refreshToken, // Keep the same refresh token\n expiresIn: response.expiresIn,\n });\n\n // Update user with tenant context\n setCurrentUser(response.user);\n sessionManager.setUser(response.user);\n setHasTenantContext(true);\n\n // Find target tenant info from userTenants\n const targetTenant = userTenants.find(t => t.id === tenantId);\n\n if (targetTenant) {\n // Use TenantProvider's switchTenant for URL handling\n switchTenant(targetTenant.subdomain, {\n tokens: {\n accessToken: response.accessToken,\n refreshToken: tokens.refreshToken,\n expiresIn: response.expiresIn,\n },\n redirectPath,\n });\n // Code after this won't execute due to page reload\n }\n };\n\n // RFC-004: Refresh user tenants from backend\n const refreshUserTenants = async (): Promise<UserTenantMembership[]> => {\n const authHeaders = await sessionManager.getAuthHeaders();\n const tenants = await authApiService.getUserTenants(authHeaders);\n setUserTenants(tenants);\n // Cache in localStorage\n try {\n localStorage.setItem('userTenants', JSON.stringify(tenants));\n } catch {\n // Ignore localStorage errors\n }\n return tenants;\n };\n\n return {\n // RFC-003: Authentication state\n isAuthenticated,\n sessionManager,\n authenticatedHttpService,\n login,\n signup,\n signupTenantAdmin,\n sendMagicLink,\n verifyMagicLink,\n changePassword,\n requestPasswordReset,\n confirmPasswordReset,\n refreshToken,\n logout,\n setTokens,\n hasValidSession,\n clearSession,\n currentUser,\n isUserLoading,\n userError,\n loadUserData,\n refreshUser,\n isAuthInitializing: !isAuthReady,\n isAuthReady,\n userRole,\n userPermissions,\n availableRoles,\n rolesLoading,\n hasPermission,\n hasAnyPermission,\n hasAllPermissions,\n getUserPermissionStrings,\n refreshRoles,\n // RFC-004: Multi-tenant user membership\n userTenants,\n hasTenantContext,\n switchToTenant,\n refreshUserTenants,\n };\n }, [\n isAuthenticated,\n sessionManager,\n authenticatedHttpService,\n authApiService,\n userApiService,\n roleApiService,\n appId,\n tenant,\n tenantSlug,\n switchTenant,\n availableRoles,\n currentUser,\n isUserLoading,\n userError,\n userTenants,\n hasTenantContext,\n isAuthReady,\n userRole,\n userPermissions,\n ]);\n\n // Keep loadUserDataRef in sync with the latest contextValue.loadUserData\n loadUserDataRef.current = contextValue.loadUserData;\n\n // Fetch roles on mount if not provided via SSR\n useEffect(() => {\n if (!config.initialRoles && appId) {\n const fetchRoles = async () => {\n try {\n setRolesLoading(true);\n const internalHttpService = new HttpService(baseUrl);\n const roleApiService = new RoleApiService(internalHttpService);\n const { roles } = await roleApiService.getRolesByApp(appId);\n setAvailableRoles(roles);\n } catch (error) {\n if (process.env.NODE_ENV === 'development') {\n console.error('[AuthProvider] Failed to fetch roles:', error);\n }\n } finally {\n setRolesLoading(false);\n }\n };\n\n fetchRoles();\n }\n }, [appId, baseUrl, config.initialRoles]);\n\n // Cross-subdomain auth: Clean URL and load user data after sync token processing\n const [urlTokensCleanedUp, setUrlTokensCleanedUp] = useState(false);\n useEffect(() => {\n if (urlTokensCleanedUp) return;\n setUrlTokensCleanedUp(true);\n\n // Clean URL if we had tokens (tokens already saved to sessionManager synchronously)\n if (initRef.current.urlTokens) {\n clearAuthTokensFromUrl();\n\n // Block auth ready until user data is loaded\n setIsLoadingAfterUrlTokens(true);\n\n contextValue\n .loadUserData()\n .catch(error => {\n if (process.env.NODE_ENV === 'development') {\n console.error('[AuthProvider] Failed to load user data after URL tokens:', error);\n }\n })\n .finally(() => {\n setIsLoadingAfterUrlTokens(false);\n });\n }\n }, [contextValue, urlTokensCleanedUp]);\n\n // Initialize user data from session on mount.\n // If a background refresh is in progress (expired token being renewed), wait for it\n // before checking session validity — prevents premature redirect to login.\n useEffect(() => {\n let cancelled = false;\n\n const init = async () => {\n // If token is expired but a refresh is pending, wait for it\n if (!sessionManager.hasValidSession() && sessionManager.getTokens()?.refreshToken) {\n await sessionManager.waitForPendingRefresh();\n }\n if (cancelled) return;\n\n const user = sessionManager.getUser();\n if (user && sessionManager.hasValidSession()) {\n setCurrentUser(user);\n }\n // Always release — valid session with no cached user will be handled\n // by the auto-load effect (triggered via contextValue change when isAuthReady flips)\n setIsRestoringSession(false);\n };\n init();\n\n return () => {\n cancelled = true;\n };\n }, [sessionManager]);\n\n // Auto-load user data if we have tokens but no currentUser\n useEffect(() => {\n // Wait until URL tokens cleanup is done before auto-loading\n if (!urlTokensCleanedUp) return;\n\n // If we had URL tokens, user data is already being loaded by the cleanup effect\n if (initRef.current.urlTokens) return;\n\n // Only trigger auto-load if we don't have currentUser and not already loading\n if (!currentUser && !isUserLoading && !userError && sessionManager.hasValidSession()) {\n loadUserDataRef\n .current()\n .catch(() => {\n // Silent fail - error already logged in loadUserData\n })\n .finally(() => {\n setIsRestoringSession(false);\n });\n } else if (currentUser) {\n setIsRestoringSession(false);\n } else {\n // No valid session or error — release\n setIsRestoringSession(false);\n }\n }, [currentUser, isUserLoading, userError, sessionManager, urlTokensCleanedUp]);\n\n return <AuthContext.Provider value={contextValue}>{children}</AuthContext.Provider>;\n}\n\nexport function useAuth(): AuthContextValue {\n const context = useContext(AuthContext);\n if (!context) {\n throw new Error('useAuth must be used within an AuthProvider');\n }\n return context;\n}\n\n/**\n * Optional hook that returns AuthContext if available, null otherwise.\n * Useful for components that may or may not be inside an AuthProvider.\n */\nexport function useAuthOptional(): AuthContextValue | null {\n return useContext(AuthContext);\n}\n","import { HttpService } from './HttpService';\nimport { SessionManager } from './SessionManager';\nimport type {\n ApiResponse,\n FeatureFlagItem,\n FeatureFlagValueResponse,\n FeatureFlag,\n CreateFeatureFlagRequest,\n PaginationParams,\n} from '../types/api';\n\nexport class FeatureFlagApiService {\n constructor(\n private httpService: HttpService,\n private sessionManager?: SessionManager\n ) {}\n\n async createFeatureFlag(request: CreateFeatureFlagRequest): Promise<FeatureFlag> {\n if (!this.sessionManager) {\n throw new Error('SessionManager is required for private endpoints');\n }\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const response = await this.httpService.post<ApiResponse<FeatureFlag>>(\n '/feature-flags/',\n request,\n {\n headers: authHeaders,\n }\n );\n return response.data;\n }\n\n async getFeatureFlags(\n params?: PaginationParams\n ): Promise<{ featureFlags: FeatureFlag[]; meta: any }> {\n if (!this.sessionManager) {\n throw new Error('SessionManager is required for private endpoints');\n }\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const queryParams = new URLSearchParams();\n\n if (params?.page) queryParams.append('page', params.page.toString());\n if (params?.limit) queryParams.append('limit', params.limit.toString());\n if (params?.sortBy) queryParams.append('sortBy', params.sortBy);\n if (params?.sortOrder) queryParams.append('sortOrder', params.sortOrder);\n\n const url = `/feature-flags/${queryParams.toString() ? `?${queryParams.toString()}` : ''}`;\n const response = await this.httpService.get<ApiResponse<FeatureFlag[]>>(url, {\n headers: authHeaders,\n });\n\n return {\n featureFlags: response.data,\n meta: response.meta,\n };\n }\n\n async getFeatureFlagById(id: string): Promise<FeatureFlag> {\n if (!this.sessionManager) {\n throw new Error('SessionManager is required for private endpoints');\n }\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const response = await this.httpService.get<ApiResponse<FeatureFlag>>(`/feature-flags/${id}`, {\n headers: authHeaders,\n });\n return response.data;\n }\n\n async updateFeatureFlag(\n id: string,\n request: Partial<CreateFeatureFlagRequest>\n ): Promise<FeatureFlag> {\n if (!this.sessionManager) {\n throw new Error('SessionManager is required for private endpoints');\n }\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const response = await this.httpService.put<ApiResponse<FeatureFlag>>(\n `/feature-flags/${id}`,\n request,\n {\n headers: authHeaders,\n }\n );\n return response.data;\n }\n\n async deleteFeatureFlag(id: string): Promise<void> {\n if (!this.sessionManager) {\n throw new Error('SessionManager is required for private endpoints');\n }\n const authHeaders = await this.sessionManager.getAuthHeaders();\n await this.httpService.delete<void>(`/feature-flags/${id}`, {\n headers: authHeaders,\n });\n }\n\n // Public endpoint - no auth required\n async getTenantFeatureFlags(tenantId: string, appId: string): Promise<FeatureFlagItem[]> {\n if (!tenantId || !appId) {\n throw new Error('Tenant ID and App ID are required');\n }\n\n const queryParams = new URLSearchParams();\n queryParams.append('tenantId', tenantId);\n queryParams.append('appId', appId);\n\n const url = `/tenant-feature-flags${queryParams.toString() ? `?${queryParams.toString()}` : ''}`;\n const response = await this.httpService.get<ApiResponse<FeatureFlagItem[]>>(url, {\n headers: { 'X-Tenant-ID': tenantId },\n });\n\n return response.data;\n }\n\n // Public endpoint - no auth required\n async getTenantFeatureFlag(\n flagKey: string,\n tenantId: string,\n appId: string\n ): Promise<FeatureFlagValueResponse> {\n if (!flagKey || !tenantId || !appId) {\n throw new Error('Flag Key, Tenant ID and App ID are required');\n }\n\n const queryParams = new URLSearchParams();\n queryParams.append('tenantId', tenantId);\n queryParams.append('appId', appId);\n\n const url = `/tenant-feature-flags/${flagKey}${queryParams.toString() ? `?${queryParams.toString()}` : ''}`;\n const response = await this.httpService.get<ApiResponse<FeatureFlagValueResponse>>(url, {\n headers: { 'X-Tenant-ID': tenantId },\n });\n\n return response.data;\n }\n}\n","import { createContext, useContext, ReactNode, useMemo, useState, useEffect } from 'react';\nimport { FeatureFlagApiService } from '../services/FeatureFlagApiService';\nimport { HttpService } from '../services/HttpService';\nimport { useAppOptional } from './AppProvider';\nimport { useTenantOptional } from './TenantProvider';\nimport type { FeatureFlagItem } from '../types/api';\n\nexport interface FeatureFlagConfig {\n refreshInterval?: number; // in milliseconds, default 5 minutes\n onError?: (error: Error) => void;\n}\n\nexport interface FeatureFlagContextValue {\n featureFlags: FeatureFlagItem[];\n loading: boolean;\n error: string | null;\n isReady: boolean;\n isEnabled: (flagName: string) => boolean;\n getFlag: (flagName: string) => FeatureFlagItem | undefined;\n refresh: () => Promise<void>;\n}\n\nconst FeatureFlagContext = createContext<FeatureFlagContextValue | null>(null);\n\ninterface FeatureFlagProviderProps {\n config?: FeatureFlagConfig;\n children: ReactNode;\n}\n\nexport function FeatureFlagProvider({ config = {}, children }: FeatureFlagProviderProps) {\n // Use optional hooks - provider works even if App/Tenant not ready yet\n const appContext = useAppOptional();\n const tenantContext = useTenantOptional();\n\n const baseUrl = appContext?.baseUrl ?? '';\n const appId = appContext?.appId ?? '';\n const tenant = tenantContext?.tenant ?? null;\n\n const [featureFlags, setFeatureFlags] = useState<FeatureFlagItem[]>([]);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [initialLoadDone, setInitialLoadDone] = useState(false);\n\n const featureFlagService = useMemo(() => {\n const httpService = new HttpService(baseUrl);\n return new FeatureFlagApiService(httpService);\n }, [baseUrl]);\n\n const fetchFeatureFlags = async () => {\n if (!tenant?.id) {\n setFeatureFlags([]);\n return;\n }\n\n setLoading(true);\n setError(null);\n\n try {\n const response = await featureFlagService.getTenantFeatureFlags(tenant.id, appId);\n setFeatureFlags(response);\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : 'Failed to fetch feature flags';\n setError(errorMessage);\n if (config.onError) {\n config.onError(err instanceof Error ? err : new Error(errorMessage));\n }\n } finally {\n setLoading(false);\n }\n };\n\n // Initial fetch and setup refresh interval\n useEffect(() => {\n // Wait for dependencies to be ready\n if (!baseUrl || !appId) return;\n\n fetchFeatureFlags().finally(() => setInitialLoadDone(true));\n\n const refreshInterval = config.refreshInterval || 5 * 60 * 1000; // 5 minutes default\n const interval = setInterval(fetchFeatureFlags, refreshInterval);\n\n return () => clearInterval(interval);\n }, [tenant?.id, baseUrl, appId, config.refreshInterval]);\n\n const contextValue = useMemo(() => {\n const isEnabled = (flagKey: string): boolean => {\n const flag = featureFlags.find(f => f.key === flagKey);\n return flag?.value === true;\n };\n\n const getFlag = (flagKey: string): FeatureFlagItem | undefined => {\n return featureFlags.find(f => f.key === flagKey);\n };\n\n const getFlagState = (flagKey: string): 'enabled' | 'disabled' | 'not_found' => {\n const flag = featureFlags.find(f => f.key === flagKey);\n if (!flag) return 'not_found';\n return flag.value ? 'enabled' : 'disabled';\n };\n\n const refresh = async () => {\n await fetchFeatureFlags();\n };\n\n // Ready when: dependencies available AND (initial load done OR no tenant needed)\n const isReady = !!(baseUrl && appId) && (initialLoadDone || !tenant?.id);\n\n return {\n featureFlags,\n loading,\n error,\n isReady,\n isEnabled,\n getFlag,\n getFlagState,\n refresh,\n };\n }, [featureFlags, loading, error, baseUrl, appId, tenant?.id, initialLoadDone]);\n\n return <FeatureFlagContext.Provider value={contextValue}>{children}</FeatureFlagContext.Provider>;\n}\n\nexport function useFeatureFlags(): FeatureFlagContextValue {\n const context = useContext(FeatureFlagContext);\n if (!context) {\n throw new Error('useFeatureFlags must be used within a FeatureFlagProvider');\n }\n return context;\n}\n\n/**\n * Optional hook that returns FeatureFlagContext if available, null otherwise.\n */\nexport function useFeatureFlagsOptional(): FeatureFlagContextValue | null {\n return useContext(FeatureFlagContext);\n}\n","import { HttpService } from './HttpService';\nimport { SessionManager } from './SessionManager';\nimport type {\n Subscription,\n CreateSubscriptionRequest,\n ApiResponse,\n TenantSubscriptionFeatures,\n} from '../types/api';\n\nexport class SubscriptionApiService {\n constructor(\n private httpService: HttpService,\n private sessionManager?: SessionManager\n ) {}\n\n async createSubscription(request: CreateSubscriptionRequest): Promise<Subscription> {\n if (!this.sessionManager) {\n throw new Error('SessionManager is required for private endpoints');\n }\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const response = await this.httpService.post<ApiResponse<Subscription>>(\n '/subscriptions/',\n request,\n {\n headers: authHeaders,\n }\n );\n return response.data;\n }\n\n async getSubscriptionById(id: string): Promise<Subscription> {\n if (!this.sessionManager) {\n throw new Error('SessionManager is required for private endpoints');\n }\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const response = await this.httpService.get<ApiResponse<Subscription>>(\n `/subscriptions/subscriptions/${id}`,\n {\n headers: authHeaders,\n }\n );\n return response.data;\n }\n\n async updateSubscription(\n id: string,\n request: Partial<CreateSubscriptionRequest>\n ): Promise<Subscription> {\n if (!this.sessionManager) {\n throw new Error('SessionManager is required for private endpoints');\n }\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const response = await this.httpService.put<ApiResponse<Subscription>>(\n `/subscriptions/${id}`,\n request,\n {\n headers: authHeaders,\n }\n );\n return response.data;\n }\n\n async changeSubscriptionPlan(subscriptionId: string, planId: string): Promise<Subscription> {\n if (!this.sessionManager) {\n throw new Error('SessionManager is required for private endpoints');\n }\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const response = await this.httpService.put<ApiResponse<Subscription>>(\n `/subscriptions/${subscriptionId}/plan`,\n { planId },\n { headers: authHeaders }\n );\n return response.data;\n }\n\n // Public endpoint - no auth required\n async getTenantSubscriptionFeatures(tenantId: string): Promise<TenantSubscriptionFeatures> {\n const response = await this.httpService.get<ApiResponse<TenantSubscriptionFeatures>>(\n `/subscriptions/tenants/${tenantId}/subscription-features`\n );\n return response.data;\n }\n\n async processPayment(subscriptionId: string, paymentData: any): Promise<any> {\n if (!this.sessionManager) {\n throw new Error('SessionManager is required for private endpoints');\n }\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const response = await this.httpService.post<ApiResponse<any>>(\n `/subscriptions/${subscriptionId}/process-payment`,\n paymentData,\n { headers: authHeaders }\n );\n return response.data;\n }\n}\n","import { createContext, useContext, ReactNode, useMemo, useState, useEffect } from 'react';\nimport { SubscriptionApiService } from '../services/SubscriptionApiService';\nimport { HttpService } from '../services/HttpService';\nimport { useAppOptional } from './AppProvider';\nimport { useTenantOptional } from './TenantProvider';\nimport type { TenantSubscriptionFeatures, PlanFeature } from '../types/api';\n\nexport interface SubscriptionConfig {\n refreshInterval?: number; // in milliseconds, default 10 minutes\n onError?: (error: Error) => void;\n}\n\nexport interface SubscriptionContextValue {\n subscription: TenantSubscriptionFeatures | null;\n features: PlanFeature[];\n loading: boolean;\n error: string | null;\n isReady: boolean;\n isFeatureEnabled: (featureKey: string) => boolean;\n getFeature: (featureKey: string) => PlanFeature | undefined;\n getFeatureValue: <T = any>(featureKey: string, defaultValue?: T) => T;\n hasAllowedPlan: (allowedPlans: string[]) => boolean;\n refresh: () => Promise<void>;\n}\n\nexport interface SubscriptionProviderProps {\n config?: SubscriptionConfig;\n children: ReactNode;\n}\n\nconst SubscriptionContext = createContext<SubscriptionContextValue | undefined>(undefined);\n\nexport function SubscriptionProvider({ config = {}, children }: SubscriptionProviderProps) {\n // Use optional hooks - provider works even if App/Tenant not ready yet\n const appContext = useAppOptional();\n const tenantContext = useTenantOptional();\n\n const baseUrl = appContext?.baseUrl ?? '';\n const tenant = tenantContext?.tenant ?? null;\n\n const [subscription, setSubscription] = useState<TenantSubscriptionFeatures | null>(null);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [initialLoadDone, setInitialLoadDone] = useState(false);\n\n // Create subscription service\n const subscriptionService = useMemo(() => {\n const httpService = new HttpService(baseUrl);\n return new SubscriptionApiService(httpService);\n }, [baseUrl]);\n\n const fetchSubscription = async () => {\n if (!tenant?.id) {\n setSubscription(null);\n return;\n }\n\n setLoading(true);\n setError(null);\n\n try {\n const response = await subscriptionService.getTenantSubscriptionFeatures(tenant.id);\n setSubscription(response);\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : 'Failed to fetch subscription';\n setError(errorMessage);\n if (config.onError) {\n config.onError(err instanceof Error ? err : new Error(errorMessage));\n }\n } finally {\n setLoading(false);\n }\n };\n\n // Fetch subscription on mount and when tenant changes\n useEffect(() => {\n // Wait for dependencies to be ready\n if (!baseUrl) return;\n\n fetchSubscription().finally(() => setInitialLoadDone(true));\n\n // Set up refresh interval if configured\n if (!config.refreshInterval) return;\n\n const refreshInterval = config.refreshInterval || 10 * 60 * 1000; // 10 minutes default\n const interval = setInterval(fetchSubscription, refreshInterval);\n\n return () => clearInterval(interval);\n }, [tenant?.id, baseUrl, config.refreshInterval]);\n\n const contextValue = useMemo(() => {\n const features = subscription?.features || [];\n\n const isFeatureEnabled = (featureKey: string): boolean => {\n const feature = features.find(f => f.key === featureKey);\n if (!feature) return false;\n\n // Handle different feature types\n if (feature.type === 'BOOLEAN' || feature.type === 'boolean') {\n return feature.value === true;\n }\n\n // For non-boolean features, consider them enabled if they have a truthy value\n return Boolean(feature.value);\n };\n\n const getFeature = (featureKey: string): PlanFeature | undefined => {\n return features.find(f => f.key === featureKey);\n };\n\n const getFeatureValue = <T = any,>(featureKey: string, defaultValue?: T): T => {\n const feature = features.find(f => f.key === featureKey);\n return feature ? feature.value : (defaultValue as T);\n };\n\n const hasAllowedPlan = (allowedPlans: string[]): boolean => {\n if (!subscription || !subscription.isActive) return false;\n\n // Check if current plan is in the allowed plans array\n return allowedPlans.includes(subscription.planId);\n };\n\n const refresh = async () => {\n await fetchSubscription();\n };\n\n // Ready when: dependencies available AND (initial load done OR no tenant needed)\n const isReady = !!baseUrl && (initialLoadDone || !tenant?.id);\n\n return {\n subscription,\n features,\n loading,\n error,\n isReady,\n isFeatureEnabled,\n getFeature,\n getFeatureValue,\n hasAllowedPlan,\n refresh,\n };\n }, [subscription, loading, error, baseUrl, tenant?.id, initialLoadDone]);\n\n return (\n <SubscriptionContext.Provider value={contextValue}>{children}</SubscriptionContext.Provider>\n );\n}\n\nexport function useSubscription(): SubscriptionContextValue {\n const context = useContext(SubscriptionContext);\n if (context === undefined) {\n throw new Error('useSubscription must be used within a SubscriptionProvider');\n }\n return context;\n}\n\n/**\n * Optional hook that returns SubscriptionContext if available, null otherwise.\n */\nexport function useSubscriptionOptional(): SubscriptionContextValue | null {\n return useContext(SubscriptionContext) ?? null;\n}\n","// Common API Response Types\nexport interface ApiResponse<T> {\n success: boolean;\n message?: string;\n data: T;\n meta?: PaginationMeta;\n}\n\nexport interface ApiError {\n success: false;\n error: {\n code: string;\n };\n message: string;\n type?: 'AUTH' | 'VALIDATION' | 'BUSINESS' | 'SYSTEM';\n}\n\nexport interface PaginationMeta {\n total: number;\n page: number;\n limit: number;\n totalPages: number;\n hasNext: boolean;\n hasPrev: boolean;\n}\n\n// User Types\nexport enum UserType {\n SUPERUSER = 'SUPERUSER',\n TENANT_ADMIN = 'TENANT_ADMIN',\n USER = 'USER',\n}\n\nexport interface User {\n id: string;\n email?: string;\n phoneNumber?: string;\n name: string;\n lastName?: string;\n isActive: boolean;\n userType: UserType;\n tenantId: string | null; // null for global token, present for tenant-scoped token\n roleId: string | null;\n role?: string | null; // User's role name in current tenant context\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface CreateUserRequest {\n email?: string;\n phoneNumber?: string;\n name: string;\n lastName?: string;\n password: string;\n tenantId: string;\n userType?: string;\n roleId?: string;\n}\n\n// Auth Types\nexport interface LoginRequest {\n username: string; // Can be email or phoneNumber\n password: string;\n appId?: string;\n tenantId?: string;\n}\n\n// Tenant membership info returned from login\nexport interface UserTenantMembership {\n id: string; // Tenant ID\n name: string; // Tenant name\n subdomain: string; // Tenant subdomain\n role: string | null; // User's role in this tenant\n}\n\nexport interface LoginResponse {\n user: User;\n accessToken: string; // Global OR tenant-scoped (depends on request)\n refreshToken: string; // Valid for switch-tenant calls\n expiresIn: number;\n tenants: UserTenantMembership[]; // User's available tenants\n}\n\n// Switch tenant types\nexport interface SwitchTenantRequest {\n refreshToken: string;\n tenantId: string;\n}\n\nexport interface SwitchTenantResponse {\n accessToken: string; // Tenant-scoped token\n expiresIn: number;\n user: User; // User WITH tenantId and role\n}\n\nexport interface SignupRequest {\n email?: string;\n phoneNumber?: string;\n name: string;\n lastName?: string;\n password: string;\n tenantId?: string;\n appId?: string;\n}\n\nexport interface ChangePasswordRequest {\n currentPassword: string;\n newPassword: string;\n}\n\nexport interface RefreshTokenRequest {\n refreshToken: string;\n}\n\nexport interface RefreshTokenResponse {\n accessToken: string;\n refreshToken: string;\n expiresIn: number;\n}\n\n// Magic Link Types\nexport interface MagicLinkRequest {\n email: string;\n tenantId: string;\n frontendUrl: string;\n name?: string; // Required for signup, optional for login\n lastName?: string;\n appId?: string;\n}\n\nexport interface MagicLinkResponse {\n message: string;\n emailSent: boolean;\n}\n\nexport interface VerifyMagicLinkRequest {\n token: string;\n email: string;\n appId: string;\n tenantId?: string;\n}\n\nexport interface VerifyMagicLinkResponse {\n user: User;\n accessToken: string;\n refreshToken: string;\n expiresIn: number;\n isNewUser: boolean; // Indicates if user was created during this process\n}\n\n// Role Types\nexport interface Role {\n id: string;\n name: string;\n description: string | null;\n appId: string;\n permissions: string[]; // API returns permissions as strings in 'resource.action' format\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface CreateRoleRequest {\n name: string;\n description?: string;\n appId: string;\n permissionIds: string[];\n}\n\nexport interface AssignRoleRequest {\n userId: string;\n}\n\n// Permission Types\nexport interface Permission {\n id: string;\n name: string;\n description: string | null;\n resource: string;\n action: string;\n appId: string | null;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface CreatePermissionRequest {\n name: string;\n description?: string;\n resource: string;\n action: string;\n appId?: string;\n}\n\n// App Types\nexport interface App {\n id: string;\n name: string;\n description: string | null;\n securityLevel: 'ADMIN' | 'USER';\n isActive: boolean;\n autoApproveTenants: boolean;\n defaultSubscriptionPlanId: string | null;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface JSONSchema {\n $schema?: string;\n $id?: string;\n title?: string;\n description?: string;\n type?: 'object' | 'array' | 'string' | 'number' | 'integer' | 'boolean' | 'null';\n properties?: { [key: string]: JSONSchema };\n items?: JSONSchema;\n required?: string[];\n enum?: unknown[];\n minimum?: number;\n maximum?: number;\n minLength?: number;\n maxLength?: number;\n pattern?: string;\n format?: string;\n default?: unknown;\n placeholder?: string;\n additionalProperties?: boolean | JSONSchema;\n}\n\nexport interface PublicAppInfo {\n id: string;\n name: string;\n description: string | null;\n settingsSchema: JSONSchema | null;\n}\n\nexport interface CreateAppRequest {\n name: string;\n description?: string;\n securityLevel?: 'ADMIN' | 'USER';\n}\n\n// Tenant Types\nexport interface Tenant {\n id: string;\n name: string;\n subdomain: string;\n isActive: boolean;\n appId: string;\n settings: Record<string, any>;\n createdAt: string;\n updatedAt: string;\n isPending?: boolean;\n}\n\nexport interface CreateTenantRequest {\n name: string;\n domain?: string;\n appId: string;\n settings?: Record<string, any>;\n}\n\nexport interface PublicTenantInfo {\n id: string;\n name: string;\n subdomain: string;\n domain?: string | null; // Legacy field\n appId: string;\n}\n\nexport interface TenantSettings {\n [key: string]: any;\n}\n\nexport interface UpdateTenantSettingsRequest {\n settings: TenantSettings;\n}\n\n// Subscription Types\nexport interface Subscription {\n id: string;\n tenantId: string;\n planId: string;\n status: 'ACTIVE' | 'INACTIVE' | 'PENDING' | 'CANCELLED';\n startDate: string;\n endDate: string | null;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface CreateSubscriptionRequest {\n tenantId: string;\n planId: string;\n startDate?: string;\n endDate?: string;\n}\n\n// Subscription Plan Types\nexport interface SubscriptionPlan {\n id: string;\n name: string;\n description: string | null;\n price: number;\n currency: string;\n billingCycle: 'MONTHLY' | 'YEARLY';\n appId: string;\n features: SubscriptionFeature[];\n isActive: boolean;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface SubscriptionFeature {\n id: string;\n name: string;\n description: string | null;\n featureType: 'BOOLEAN' | 'NUMERIC' | 'TEXT';\n value: any;\n planId: string;\n}\n\nexport interface CreateSubscriptionPlanRequest {\n name: string;\n description?: string;\n price: number;\n currency: string;\n billingCycle: 'MONTHLY' | 'YEARLY';\n appId: string;\n features: CreateSubscriptionFeatureRequest[];\n}\n\nexport interface CreateSubscriptionFeatureRequest {\n name: string;\n description?: string;\n featureType: 'BOOLEAN' | 'NUMERIC' | 'TEXT';\n value: any;\n}\n\n// Feature Flag Types\nexport interface FeatureFlag {\n id: string;\n name: string;\n description: string | null;\n isActive: boolean;\n appId: string;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface CreateFeatureFlagRequest {\n name: string;\n description?: string;\n appId: string;\n}\n\n// Feature Flag API Response Types\nexport interface FeatureFlagItem {\n featureFlagId: string;\n key: string;\n name: string;\n value: boolean;\n isOverridden: boolean;\n}\n\nexport interface FeatureFlagValueResponse {\n key: string;\n value: boolean;\n}\n\n// Subscription Feature Types\nexport interface PlanFeature {\n key: string;\n name: string;\n type: 'BOOLEAN' | 'NUMBER' | 'STRING' | 'boolean' | 'number' | 'string';\n value: any;\n description?: string;\n}\n\nexport interface TenantSubscriptionFeatures {\n tenantId: string;\n planId: string;\n planName: string;\n subscriptionStatus: string;\n features: PlanFeature[];\n isActive: boolean;\n}\n\n// Common Query Parameters\nexport interface PaginationParams {\n page?: number;\n limit?: number;\n sortBy?: string;\n sortOrder?: 'ASC' | 'DESC';\n}\n","import { ReactNode } from 'react';\nimport { UserType } from './api';\n\n/**\n * Access mode for zone requirements\n * - 'required': Must match condition (redirect if not)\n * - 'forbidden': Must NOT match condition (redirect if matches)\n * - 'optional': Can be either (no redirect)\n */\nexport type AccessMode = 'required' | 'forbidden' | 'optional';\n\n/**\n * Reason types for access denial\n */\nexport type AccessDeniedType =\n | 'no_tenant'\n | 'has_tenant'\n | 'not_authenticated'\n | 'already_authenticated'\n | 'wrong_user_type'\n | 'missing_permissions';\n\n/**\n * Detailed reason object passed to callbacks when access is denied\n */\nexport interface AccessDeniedReason {\n type: AccessDeniedType;\n required?: {\n tenant?: AccessMode;\n auth?: AccessMode;\n userType?: UserType | UserType[];\n permissions?: string[];\n };\n current?: {\n hasTenant: boolean;\n isAuthenticated: boolean;\n userType?: UserType;\n permissions?: string[];\n };\n redirectTo: string;\n}\n\n/**\n * Zone root paths for automatic redirects\n */\nexport interface ZoneRoots {\n publicGuest?: string;\n publicUser?: string;\n publicAdmin?: string;\n tenantGuest?: string;\n tenantUser?: string;\n tenantAdmin?: string;\n default?: string;\n}\n\n/**\n * Default zone roots\n */\nexport const DEFAULT_ZONE_ROOTS: Required<ZoneRoots> = {\n publicGuest: '/',\n publicUser: '/account',\n publicAdmin: '/admin',\n tenantGuest: '/login',\n tenantUser: '/dashboard',\n tenantAdmin: '/admin/dashboard',\n default: '/',\n};\n\n/**\n * Preset zone configuration\n */\nexport interface ZonePresetConfig {\n tenant?: AccessMode;\n auth?: AccessMode;\n userType?: UserType | UserType[];\n requiredPermissions?: string[];\n}\n\n/**\n * Built-in zone presets\n */\nexport interface ZonePresets {\n // Public/Landing zones\n landing: ZonePresetConfig;\n publicOnly: ZonePresetConfig;\n\n // Auth zones\n login: ZonePresetConfig;\n guest: ZonePresetConfig;\n authenticated: ZonePresetConfig;\n\n // Tenant zones\n tenant: ZonePresetConfig;\n tenantOpen: ZonePresetConfig;\n tenantAuth: ZonePresetConfig;\n\n // User type zones\n user: ZonePresetConfig;\n admin: ZonePresetConfig;\n\n // Fully open\n open: ZonePresetConfig;\n}\n\n/**\n * Default preset configurations\n */\nexport const DEFAULT_ZONE_PRESETS: ZonePresets = {\n // Public/Landing zones\n landing: { tenant: 'forbidden', auth: 'optional' },\n publicOnly: { tenant: 'forbidden', auth: 'forbidden' },\n\n // Auth zones\n login: { tenant: 'required', auth: 'forbidden' },\n guest: { auth: 'forbidden' },\n authenticated: { auth: 'required' },\n\n // Tenant zones\n tenant: { tenant: 'required' },\n tenantOpen: { tenant: 'required', auth: 'optional' },\n tenantAuth: { tenant: 'required', auth: 'required' },\n\n // User type zones\n user: { tenant: 'required', auth: 'required', userType: UserType.USER },\n admin: { tenant: 'required', auth: 'required', userType: UserType.TENANT_ADMIN },\n\n // Fully open\n open: { tenant: 'optional', auth: 'optional' },\n};\n\n/**\n * Return URL storage options\n */\nexport type ReturnToStorage = 'url' | 'session' | 'local';\n\n/**\n * Routing configuration for AuthProvider\n */\nexport interface RoutingConfig {\n zoneRoots?: ZoneRoots;\n presets?: Partial<ZonePresets>;\n loadingFallback?: ReactNode;\n accessDeniedFallback?: ReactNode;\n onAccessDenied?: (reason: AccessDeniedReason) => void;\n returnToParam?: string;\n returnToStorage?: ReturnToStorage;\n}\n\n/**\n * Props for ZoneRoute component\n */\nexport interface ZoneRouteProps {\n children: ReactNode;\n\n // Preset (shorthand for common configurations)\n preset?: keyof ZonePresets | string;\n\n // Zone requirements with access modes\n tenant?: AccessMode;\n auth?: AccessMode;\n\n // User type requirements (only applies when auth='required')\n userType?: UserType | UserType[];\n\n // Permission requirements\n requiredPermissions?: string[];\n requireAllPermissions?: boolean;\n\n // Return URL handling\n returnTo?: boolean | string;\n\n // Callbacks\n onAccessDenied?: (reason: AccessDeniedReason) => void;\n\n // Fallback states (override global config)\n redirectTo?: string;\n loadingFallback?: ReactNode;\n accessDeniedFallback?: ReactNode;\n}\n\n/**\n * Zone navigation hook return type\n */\nexport interface UseZoneNavigationReturn {\n returnToUrl: string | null;\n clearReturnTo: () => void;\n setReturnTo: (url: string) => void;\n navigateToZone: (zone: keyof ZoneRoots) => void;\n getSmartRedirect: () => string;\n}\n","import { createContext, useContext, ReactNode, useMemo } from 'react';\nimport {\n RoutingConfig,\n ZoneRoots,\n ZonePresets,\n ZonePresetConfig,\n AccessDeniedReason,\n ReturnToStorage,\n DEFAULT_ZONE_ROOTS,\n DEFAULT_ZONE_PRESETS,\n} from '../types/zoneRouting';\n\n/**\n * Context value provided by RoutingProvider\n */\nexport interface RoutingContextValue {\n zoneRoots: Required<ZoneRoots>;\n presets: ZonePresets;\n loadingFallback: ReactNode;\n accessDeniedFallback: ReactNode;\n onAccessDenied?: (reason: AccessDeniedReason) => void;\n returnToParam: string;\n returnToStorage: ReturnToStorage;\n}\n\nconst RoutingContext = createContext<RoutingContextValue | null>(null);\n\nexport interface RoutingProviderProps {\n config?: RoutingConfig;\n children: ReactNode;\n}\n\n/**\n * RoutingProvider - Global configuration for zone-based routing\n *\n * Provides default redirect paths, presets, and fallbacks that ZoneRoute\n * components will use. Can be customized per-application.\n *\n * @example\n * ```tsx\n * <RoutingProvider config={{\n * zoneRoots: {\n * tenantGuest: '/auth/login',\n * tenantUser: '/app/dashboard',\n * tenantAdmin: '/admin',\n * },\n * loadingFallback: <Spinner />,\n * onAccessDenied: (reason) => console.log('Access denied:', reason),\n * }}>\n * <App />\n * </RoutingProvider>\n * ```\n */\nexport function RoutingProvider({ config = {}, children }: RoutingProviderProps) {\n const contextValue = useMemo<RoutingContextValue>(() => {\n // Merge user config with defaults\n const zoneRoots: Required<ZoneRoots> = {\n ...DEFAULT_ZONE_ROOTS,\n ...config.zoneRoots,\n };\n\n const presets: ZonePresets = {\n ...DEFAULT_ZONE_PRESETS,\n ...config.presets,\n };\n\n return {\n zoneRoots,\n presets,\n loadingFallback: config.loadingFallback ?? null,\n accessDeniedFallback: config.accessDeniedFallback ?? null,\n onAccessDenied: config.onAccessDenied,\n returnToParam: config.returnToParam ?? 'returnTo',\n returnToStorage: config.returnToStorage ?? 'url',\n };\n }, [config]);\n\n return <RoutingContext.Provider value={contextValue}>{children}</RoutingContext.Provider>;\n}\n\n/**\n * Hook to access routing configuration\n * @throws Error if used outside RoutingProvider\n */\nexport function useRouting(): RoutingContextValue {\n const context = useContext(RoutingContext);\n if (!context) {\n throw new Error('useRouting must be used within a RoutingProvider');\n }\n return context;\n}\n\n/**\n * Optional hook that returns RoutingContext if available, defaults otherwise.\n * Useful for ZoneRoute which should work with or without RoutingProvider.\n */\nexport function useRoutingOptional(): RoutingContextValue {\n const context = useContext(RoutingContext);\n\n // Return defaults if no provider\n if (!context) {\n return {\n zoneRoots: DEFAULT_ZONE_ROOTS,\n presets: DEFAULT_ZONE_PRESETS,\n loadingFallback: null,\n accessDeniedFallback: null,\n onAccessDenied: undefined,\n returnToParam: 'returnTo',\n returnToStorage: 'url',\n };\n }\n\n return context;\n}\n\n/**\n * Get a specific preset configuration\n */\nexport function useZonePreset(\n presetName: keyof ZonePresets | string\n): ZonePresetConfig | undefined {\n const { presets } = useRoutingOptional();\n return presets[presetName as keyof ZonePresets];\n}\n","import { ReactNode } from 'react';\nimport { useAuth } from '../providers/AuthProvider';\nimport { UserType, Permission } from '../types/api';\n\nexport interface ProtectedProps {\n children: ReactNode;\n fallback?: ReactNode;\n minUserType?: UserType;\n requiredPermissions?: (string | Permission)[];\n requireAllPermissions?: boolean; // If true, user must have ALL permissions. If false, user needs ANY permission\n}\n\nconst DefaultFallback = () => (\n <div\n style={{\n display: 'flex',\n flexDirection: 'column',\n justifyContent: 'center',\n alignItems: 'center',\n padding: '20px',\n backgroundColor: '#f8f9fa',\n border: '1px solid #dee2e6',\n borderRadius: '6px',\n textAlign: 'center',\n margin: '20px 0',\n }}\n >\n <div style={{ fontSize: '2rem', marginBottom: '10px' }}>🔒</div>\n <h3 style={{ color: '#495057', marginBottom: '10px' }}>Access Required</h3>\n <p style={{ color: '#6c757d', fontSize: '14px', marginBottom: '15px' }}>\n You need to be signed in to view this content.\n </p>\n <button\n style={{\n padding: '8px 16px',\n backgroundColor: '#007bff',\n color: 'white',\n border: 'none',\n borderRadius: '4px',\n cursor: 'pointer',\n fontSize: '14px',\n }}\n onClick={() => (window.location.href = '/login')}\n >\n Sign In\n </button>\n </div>\n);\n\nconst InsufficientPermissionsFallback = ({\n userType,\n minUserType,\n missingPermissions,\n}: {\n userType?: UserType;\n minUserType?: UserType;\n missingPermissions?: string[];\n}) => (\n <div\n style={{\n display: 'flex',\n flexDirection: 'column',\n justifyContent: 'center',\n alignItems: 'center',\n padding: '20px',\n backgroundColor: '#fff3cd',\n border: '1px solid #ffeaa7',\n borderRadius: '6px',\n textAlign: 'center',\n margin: '20px 0',\n }}\n >\n <div style={{ fontSize: '2rem', marginBottom: '10px' }}>⚠️</div>\n <h3 style={{ color: '#856404', marginBottom: '10px' }}>Insufficient Permissions</h3>\n {minUserType && userType ? (\n <>\n <p style={{ color: '#856404', fontSize: '14px', marginBottom: '10px' }}>\n This content requires <strong>{minUserType}</strong> access level or higher.\n </p>\n <p style={{ color: '#6c757d', fontSize: '12px' }}>\n Your current access level: <strong>{userType}</strong>\n </p>\n </>\n ) : (\n <>\n <p style={{ color: '#856404', fontSize: '14px', marginBottom: '10px' }}>\n You don't have the required permissions to view this content.\n </p>\n {missingPermissions && missingPermissions.length > 0 && (\n <p style={{ color: '#6c757d', fontSize: '12px' }}>\n Required permissions: <strong>{missingPermissions.join(', ')}</strong>\n </p>\n )}\n </>\n )}\n </div>\n);\n\n// Helper function to check if user type meets minimum requirement\nconst hasMinimumUserType = (userType: UserType, minUserType: UserType): boolean => {\n const hierarchy = {\n [UserType.USER]: 1,\n [UserType.TENANT_ADMIN]: 2,\n [UserType.SUPERUSER]: 3,\n };\n\n return hierarchy[userType] >= hierarchy[minUserType];\n};\n\nexport function Protected({\n children,\n fallback,\n minUserType,\n requiredPermissions,\n requireAllPermissions = false,\n}: ProtectedProps) {\n const { hasValidSession, sessionManager, hasPermission, hasAnyPermission, hasAllPermissions } =\n useAuth();\n\n // Check if user has a valid session\n if (!hasValidSession()) {\n return <>{fallback || <DefaultFallback />}</>;\n }\n\n const user = sessionManager.getUser();\n\n if (!user) {\n // User session exists but no user data - show fallback\n return <>{fallback || <DefaultFallback />}</>;\n }\n\n // Check user type permissions if specified\n if (minUserType && !hasMinimumUserType(user.userType, minUserType)) {\n return <InsufficientPermissionsFallback userType={user.userType} minUserType={minUserType} />;\n }\n\n // Check specific permissions if specified\n if (requiredPermissions && requiredPermissions.length > 0) {\n const hasRequiredPermissions = requireAllPermissions\n ? hasAllPermissions(requiredPermissions)\n : hasAnyPermission(requiredPermissions);\n\n if (!hasRequiredPermissions) {\n // Get missing permissions for better error message\n const missingPermissions = requiredPermissions\n .filter(permission => !hasPermission(permission))\n .map(permission => (typeof permission === 'string' ? permission : permission.name));\n\n return <InsufficientPermissionsFallback missingPermissions={missingPermissions} />;\n }\n }\n\n // User is authenticated and has sufficient permissions\n return <>{children}</>;\n}\n","import { ReactNode, useEffect } from 'react';\nimport { Navigate, useLocation } from 'react-router-dom';\nimport { useAuth } from '../providers/AuthProvider';\nimport { UserType, Permission } from '../types/api';\n\nexport interface ProtectedRouteProps {\n children: ReactNode;\n redirectTo?: string;\n requiredUserType?: UserType; // If set, only users with this exact user type can access\n requiredPermissions?: (string | Permission)[];\n requireAllPermissions?: boolean; // If true, user must have ALL permissions. If false, user needs ANY permission\n fallback?: ReactNode;\n}\n\nconst DefaultFallback = ({ redirectPath }: { redirectPath: string }) => (\n <div\n style={{\n display: 'flex',\n flexDirection: 'column',\n justifyContent: 'center',\n alignItems: 'center',\n minHeight: '100vh',\n padding: '2rem',\n backgroundColor: '#f9fafb',\n textAlign: 'center',\n }}\n >\n <div\n style={{\n backgroundColor: '#ffffff',\n padding: '2rem',\n borderRadius: '8px',\n boxShadow: '0 2px 10px rgba(0, 0, 0, 0.1)',\n maxWidth: '400px',\n }}\n >\n <div style={{ fontSize: '3rem', marginBottom: '1rem' }}>🔒</div>\n <h2 style={{ color: '#374151', marginBottom: '1rem' }}>Access Required</h2>\n <p style={{ color: '#6b7280', marginBottom: '1.5rem' }}>\n You need to be signed in to access this page.\n </p>\n <p style={{ fontSize: '0.875rem', color: '#9ca3af' }}>Redirecting to {redirectPath}...</p>\n </div>\n </div>\n);\n\nconst InsufficientPermissionsFallback = ({\n userType,\n requiredUserType,\n missingPermissions,\n}: {\n userType?: UserType;\n requiredUserType?: UserType;\n missingPermissions?: string[];\n}) => (\n <div\n style={{\n display: 'flex',\n flexDirection: 'column',\n justifyContent: 'center',\n alignItems: 'center',\n minHeight: '100vh',\n padding: '2rem',\n backgroundColor: '#f9fafb',\n textAlign: 'center',\n }}\n >\n <div\n style={{\n backgroundColor: '#ffffff',\n padding: '2rem',\n borderRadius: '8px',\n boxShadow: '0 2px 10px rgba(0, 0, 0, 0.1)',\n maxWidth: '400px',\n }}\n >\n <div style={{ fontSize: '3rem', marginBottom: '1rem' }}>⚠️</div>\n <h2 style={{ color: '#374151', marginBottom: '1rem' }}>Insufficient Permissions</h2>\n {requiredUserType && userType ? (\n <>\n <p style={{ color: '#6b7280', marginBottom: '1rem' }}>\n This page requires <strong>{requiredUserType}</strong> access.\n </p>\n <p style={{ color: '#9ca3af', fontSize: '0.875rem' }}>\n Your current user type: <strong>{userType}</strong>\n </p>\n </>\n ) : (\n <>\n <p style={{ color: '#6b7280', marginBottom: '1rem' }}>\n You don't have the required permissions to access this page.\n </p>\n {missingPermissions && missingPermissions.length > 0 && (\n <p style={{ color: '#9ca3af', fontSize: '0.875rem' }}>\n Required permissions: <strong>{missingPermissions.join(', ')}</strong>\n </p>\n )}\n </>\n )}\n </div>\n </div>\n);\n\n// Helper function to check if user type matches the required type\nconst hasRequiredUserType = (userType: UserType, requiredUserType: UserType): boolean => {\n return userType === requiredUserType;\n};\n\n/**\n * @deprecated Use `AuthenticatedZone` or `AdminZone` from './ZoneRoute' instead.\n * ProtectedRoute will be removed in a future version.\n *\n * Migration:\n * ```tsx\n * // Before\n * <ProtectedRoute redirectTo=\"/login\"><Page /></ProtectedRoute>\n *\n * // After\n * <AuthenticatedZone redirectTo=\"/login\"><Page /></AuthenticatedZone>\n *\n * // For admin routes:\n * // Before\n * <ProtectedRoute requiredUserType=\"TENANT_ADMIN\"><Admin /></ProtectedRoute>\n *\n * // After\n * <AdminZone><Admin /></AdminZone>\n * ```\n */\nexport function ProtectedRoute({\n children,\n redirectTo = '/login',\n requiredUserType,\n requiredPermissions,\n requireAllPermissions = false,\n fallback,\n}: ProtectedRouteProps) {\n const { hasValidSession, sessionManager, hasPermission, hasAnyPermission, hasAllPermissions } =\n useAuth();\n const location = useLocation();\n\n useEffect(() => {\n if (process.env.NODE_ENV === 'development') {\n console.warn(\n '[react-identity-access] ProtectedRoute is deprecated. Use AuthenticatedZone or AdminZone from ZoneRoute instead.'\n );\n }\n }, []);\n\n // Check if user has a valid session\n if (!hasValidSession()) {\n if (fallback) {\n return <>{fallback}</>;\n }\n\n return (\n <>\n <DefaultFallback redirectPath={redirectTo} />\n <Navigate to={redirectTo} state={{ from: location.pathname }} replace />\n </>\n );\n }\n\n const user = sessionManager.getUser();\n\n if (!user) {\n // User session exists but no user data - redirect to login\n return <Navigate to={redirectTo} state={{ from: location.pathname }} replace />;\n }\n\n // Check user type if specified\n if (requiredUserType && !hasRequiredUserType(user.userType, requiredUserType)) {\n return (\n <InsufficientPermissionsFallback\n userType={user.userType}\n requiredUserType={requiredUserType}\n />\n );\n }\n\n // Check specific permissions if specified\n if (requiredPermissions && requiredPermissions.length > 0) {\n const hasRequiredPermissions = requireAllPermissions\n ? hasAllPermissions(requiredPermissions)\n : hasAnyPermission(requiredPermissions);\n\n if (!hasRequiredPermissions) {\n // Get missing permissions for better error message\n const missingPermissions = requiredPermissions\n .filter(permission => !hasPermission(permission))\n .map(permission => (typeof permission === 'string' ? permission : permission.name));\n\n return <InsufficientPermissionsFallback missingPermissions={missingPermissions} />;\n }\n }\n\n // User is authenticated and has sufficient permissions\n return <>{children}</>;\n}\n","import { ReactNode, useEffect } from 'react';\nimport { Navigate, useLocation } from 'react-router-dom';\nimport { useTenantInfo } from '../providers/TenantProvider';\n\nexport interface TenantRouteProps {\n children: ReactNode;\n redirectTo?: string;\n fallback?: ReactNode;\n}\n\nconst DefaultTenantRequiredFallback = ({ redirectPath }: { redirectPath: string }) => (\n <div\n style={{\n display: 'flex',\n flexDirection: 'column',\n justifyContent: 'center',\n alignItems: 'center',\n minHeight: '100vh',\n padding: '2rem',\n backgroundColor: '#f9fafb',\n textAlign: 'center',\n }}\n >\n <div\n style={{\n backgroundColor: '#ffffff',\n padding: '2rem',\n borderRadius: '8px',\n boxShadow: '0 2px 10px rgba(0, 0, 0, 0.1)',\n maxWidth: '400px',\n }}\n >\n <div style={{ fontSize: '3rem', marginBottom: '1rem' }}>🏢</div>\n <h2 style={{ color: '#374151', marginBottom: '1rem' }}>Tenant Required</h2>\n <p style={{ color: '#6b7280', marginBottom: '1.5rem' }}>\n This page requires a tenant context to access.\n </p>\n <p style={{ fontSize: '0.875rem', color: '#9ca3af' }}>Redirecting to {redirectPath}...</p>\n </div>\n </div>\n);\n\n/**\n * @deprecated Use `TenantZone` from './ZoneRoute' instead.\n * TenantRoute will be removed in a future version.\n *\n * Migration:\n * ```tsx\n * // Before\n * <TenantRoute redirectTo=\"/\"><Page /></TenantRoute>\n *\n * // After\n * <TenantZone redirectTo=\"/\"><Page /></TenantZone>\n * ```\n */\nexport function TenantRoute({ children, redirectTo = '/', fallback }: TenantRouteProps) {\n const { tenant, isLoading, error } = useTenantInfo();\n const location = useLocation();\n\n useEffect(() => {\n if (process.env.NODE_ENV === 'development') {\n console.warn(\n '[react-identity-access] TenantRoute is deprecated. Use TenantZone from ZoneRoute instead.'\n );\n }\n }, []);\n\n // Show loading state while tenant is being detected/loaded\n if (isLoading) {\n return null; // Let TenantProvider handle loading fallback\n }\n\n // If there's an error loading tenant, let TenantProvider handle it\n if (error) {\n return null; // Let TenantProvider handle error fallback\n }\n\n // Check if tenant is required but not present\n if (!tenant) {\n if (fallback) {\n return <>{fallback}</>;\n }\n\n return (\n <>\n <DefaultTenantRequiredFallback redirectPath={redirectTo} />\n <Navigate to={redirectTo} state={{ from: location.pathname }} replace />\n </>\n );\n }\n\n // Tenant is present, render children\n return <>{children}</>;\n}\n","import { ReactNode, useEffect } from 'react';\nimport { Navigate, useLocation } from 'react-router-dom';\nimport { useTenantInfo } from '../providers/TenantProvider';\n\nexport interface LandingRouteProps {\n children: ReactNode;\n redirectTo?: string;\n fallback?: ReactNode;\n}\n\nconst DefaultTenantDetectedFallback = ({ redirectPath }: { redirectPath: string }) => (\n <div\n style={{\n display: 'flex',\n flexDirection: 'column',\n justifyContent: 'center',\n alignItems: 'center',\n minHeight: '100vh',\n padding: '2rem',\n backgroundColor: '#f9fafb',\n textAlign: 'center',\n }}\n >\n <div\n style={{\n backgroundColor: '#ffffff',\n padding: '2rem',\n borderRadius: '8px',\n boxShadow: '0 2px 10px rgba(0, 0, 0, 0.1)',\n maxWidth: '400px',\n }}\n >\n <div style={{ fontSize: '3rem', marginBottom: '1rem' }}>🚀</div>\n <h2 style={{ color: '#374151', marginBottom: '1rem' }}>Tenant Detected</h2>\n <p style={{ color: '#6b7280', marginBottom: '1.5rem' }}>\n You are accessing a tenant-specific context. Redirecting to the appropriate page.\n </p>\n <p style={{ fontSize: '0.875rem', color: '#9ca3af' }}>Redirecting to {redirectPath}...</p>\n </div>\n </div>\n);\n\n/**\n * @deprecated Use `PublicZone` from './ZoneRoute' instead.\n * LandingRoute will be removed in a future version.\n *\n * Migration:\n * ```tsx\n * // Before\n * <LandingRoute redirectTo=\"/dashboard\"><Page /></LandingRoute>\n *\n * // After\n * <PublicZone redirectTo=\"/dashboard\"><Page /></PublicZone>\n * ```\n */\nexport function LandingRoute({ children, redirectTo = '/dashboard', fallback }: LandingRouteProps) {\n const { tenant, isLoading, error } = useTenantInfo();\n const location = useLocation();\n\n useEffect(() => {\n if (process.env.NODE_ENV === 'development') {\n console.warn(\n '[react-identity-access] LandingRoute is deprecated. Use PublicZone from ZoneRoute instead.'\n );\n }\n }, []);\n\n // Show loading state while tenant is being detected/loaded\n if (isLoading) {\n return null; // Let TenantProvider handle loading fallback\n }\n\n // If there's an error loading tenant, let TenantProvider handle it\n if (error) {\n return null; // Let TenantProvider handle error fallback\n }\n\n // Check if tenant is present (should redirect to tenant-specific route)\n if (tenant) {\n if (fallback) {\n return <>{fallback}</>;\n }\n\n return (\n <>\n <DefaultTenantDetectedFallback redirectPath={redirectTo} />\n <Navigate to={redirectTo} state={{ from: location.pathname }} replace />\n </>\n );\n }\n\n // No tenant present, render public landing page\n return <>{children}</>;\n}\n","import { FC, useEffect, useMemo } from 'react';\nimport { Navigate, useLocation } from 'react-router-dom';\nimport { useAuth } from '../providers/AuthProvider';\nimport { useTenant } from '../providers/TenantProvider';\nimport { useRoutingOptional } from '../providers/RoutingProvider';\nimport { UserType } from '../types/api';\nimport {\n AccessMode,\n AccessDeniedReason,\n AccessDeniedType,\n ZoneRouteProps,\n ZonePresetConfig,\n ZoneRoots,\n ReturnToStorage,\n} from '../types/zoneRouting';\n\ninterface ZoneState {\n hasTenant: boolean;\n isAuthenticated: boolean;\n userType?: UserType;\n permissions: string[];\n isLoading: boolean;\n}\n\n/**\n * Check if user type matches requirement\n */\nfunction matchesUserType(\n currentType: UserType | undefined,\n required: UserType | UserType[] | undefined\n): boolean {\n if (!required) return true;\n if (!currentType) return false;\n\n if (Array.isArray(required)) {\n return required.includes(currentType);\n }\n return currentType === required;\n}\n\n/**\n * Check access mode condition\n */\nfunction checkAccessMode(\n mode: AccessMode | undefined,\n condition: boolean\n): 'pass' | 'fail' | 'skip' {\n if (!mode || mode === 'optional') return 'skip';\n if (mode === 'required') return condition ? 'pass' : 'fail';\n if (mode === 'forbidden') return condition ? 'fail' : 'pass';\n return 'skip';\n}\n\n/**\n * Determine access denied type based on requirements and state\n */\nfunction getAccessDeniedType(\n requirements: {\n tenant?: AccessMode;\n auth?: AccessMode;\n userType?: UserType | UserType[];\n permissions?: string[];\n requireAllPermissions?: boolean;\n },\n state: ZoneState\n): AccessDeniedType | null {\n // Check tenant requirement\n const tenantCheck = checkAccessMode(requirements.tenant, state.hasTenant);\n if (tenantCheck === 'fail') {\n return state.hasTenant ? 'has_tenant' : 'no_tenant';\n }\n\n // Check auth requirement\n const authCheck = checkAccessMode(requirements.auth, state.isAuthenticated);\n if (authCheck === 'fail') {\n return state.isAuthenticated ? 'already_authenticated' : 'not_authenticated';\n }\n\n // Check user type (only if auth is required or optional with authenticated user)\n if (requirements.userType && state.isAuthenticated) {\n if (!matchesUserType(state.userType, requirements.userType)) {\n return 'wrong_user_type';\n }\n }\n\n // Check permissions\n if (requirements.permissions && requirements.permissions.length > 0) {\n const checkFn =\n requirements.requireAllPermissions !== false\n ? (perms: string[]) => perms.every(p => state.permissions.includes(p))\n : (perms: string[]) => perms.some(p => state.permissions.includes(p));\n if (!checkFn(requirements.permissions)) {\n return 'missing_permissions';\n }\n }\n\n return null;\n}\n\n/**\n * Get smart redirect based on current state\n */\nfunction getSmartRedirect(state: ZoneState, zoneRoots: Required<ZoneRoots>): string {\n if (!state.hasTenant) {\n if (!state.isAuthenticated) return zoneRoots.publicGuest;\n if (state.userType === UserType.TENANT_ADMIN) return zoneRoots.publicAdmin;\n return zoneRoots.publicUser;\n } else {\n if (!state.isAuthenticated) return zoneRoots.tenantGuest;\n if (state.userType === UserType.TENANT_ADMIN) return zoneRoots.tenantAdmin;\n return zoneRoots.tenantUser;\n }\n}\n\n/**\n * Build redirect URL with return path\n */\nfunction buildRedirectUrl(\n redirectTo: string,\n returnPath: string | boolean | undefined,\n currentPath: string,\n returnToParam: string,\n returnToStorage: ReturnToStorage\n): string {\n if (!returnPath || returnToStorage !== 'url') {\n return redirectTo;\n }\n\n const returnUrl = typeof returnPath === 'string' ? returnPath : currentPath;\n const separator = redirectTo.includes('?') ? '&' : '?';\n return `${redirectTo}${separator}${returnToParam}=${encodeURIComponent(returnUrl)}`;\n}\n\n/**\n * Store return URL in session/local storage\n */\nfunction storeReturnUrl(\n returnPath: string | boolean | undefined,\n currentPath: string,\n returnToStorage: ReturnToStorage\n): void {\n if (!returnPath || returnToStorage === 'url') return;\n\n const returnUrl = typeof returnPath === 'string' ? returnPath : currentPath;\n const key = 'zone_return_to';\n\n if (returnToStorage === 'session') {\n sessionStorage.setItem(key, returnUrl);\n } else if (returnToStorage === 'local') {\n localStorage.setItem(key, returnUrl);\n }\n}\n\n/**\n * ZoneRoute - Unified route protection component\n *\n * Replaces TenantRoute, LandingRoute, and ProtectedRoute with a single\n * flexible component that supports:\n * - Tenant context requirements (required/forbidden/optional)\n * - Authentication requirements (required/forbidden/optional)\n * - User type filtering\n * - Permission checks\n * - Smart redirects\n * - Return URL handling\n */\nexport const ZoneRoute: FC<ZoneRouteProps> = ({\n children,\n preset,\n tenant: tenantMode,\n auth: authMode,\n userType,\n requiredPermissions,\n requireAllPermissions = true,\n returnTo,\n onAccessDenied,\n redirectTo,\n loadingFallback,\n accessDeniedFallback,\n}) => {\n const location = useLocation();\n const { isAuthenticated, isAuthInitializing, currentUser, userPermissions } = useAuth();\n const { tenant, isTenantLoading } = useTenant();\n\n // Get global routing config (works with or without RoutingProvider)\n const routingConfig = useRoutingOptional();\n\n // Resolve preset configuration from global config\n const presetConfig: ZonePresetConfig | undefined = useMemo(() => {\n if (!preset) return undefined;\n return routingConfig.presets[preset as keyof typeof routingConfig.presets];\n }, [preset, routingConfig.presets]);\n\n // Merge preset with explicit props (explicit props take precedence)\n const requirements = useMemo(\n () => ({\n tenant: tenantMode ?? presetConfig?.tenant,\n auth: authMode ?? presetConfig?.auth,\n userType: userType ?? presetConfig?.userType,\n permissions: requiredPermissions ?? presetConfig?.requiredPermissions,\n requireAllPermissions,\n }),\n [tenantMode, authMode, userType, requiredPermissions, presetConfig, requireAllPermissions]\n );\n\n // Current state\n const state: ZoneState = useMemo(\n () => ({\n hasTenant: Boolean(tenant),\n isAuthenticated,\n userType: currentUser?.userType,\n permissions: userPermissions,\n isLoading: isAuthInitializing || isTenantLoading,\n }),\n [\n tenant,\n isAuthenticated,\n currentUser?.userType,\n userPermissions,\n isAuthInitializing,\n isTenantLoading,\n ]\n );\n\n // Check access\n const accessDeniedType = useMemo(() => {\n if (state.isLoading) return null;\n return getAccessDeniedType(requirements, state);\n }, [requirements, state]);\n\n // Calculate redirect target using global config\n const redirectTarget = useMemo(() => {\n if (!accessDeniedType) return null;\n return redirectTo || getSmartRedirect(state, routingConfig.zoneRoots);\n }, [accessDeniedType, redirectTo, state, routingConfig.zoneRoots]);\n\n // Build access denied reason\n const accessDeniedReason: AccessDeniedReason | null = useMemo(() => {\n if (!accessDeniedType || !redirectTarget) return null;\n return {\n type: accessDeniedType,\n required: {\n tenant: requirements.tenant,\n auth: requirements.auth,\n userType: requirements.userType,\n permissions: requirements.permissions,\n },\n current: {\n hasTenant: state.hasTenant,\n isAuthenticated: state.isAuthenticated,\n userType: state.userType,\n permissions: state.permissions,\n },\n redirectTo: redirectTarget,\n };\n }, [accessDeniedType, redirectTarget, requirements, state]);\n\n // Call onAccessDenied callback (local or global)\n useEffect(() => {\n if (accessDeniedReason) {\n // Local callback takes precedence\n if (onAccessDenied) {\n onAccessDenied(accessDeniedReason);\n } else if (routingConfig.onAccessDenied) {\n routingConfig.onAccessDenied(accessDeniedReason);\n }\n }\n }, [accessDeniedReason, onAccessDenied, routingConfig]);\n\n // Handle return URL storage for non-URL storage modes\n useEffect(() => {\n if (accessDeniedReason && returnTo) {\n storeReturnUrl(returnTo, location.pathname + location.search, routingConfig.returnToStorage);\n }\n }, [\n accessDeniedReason,\n returnTo,\n location.pathname,\n location.search,\n routingConfig.returnToStorage,\n ]);\n\n // Loading state (local fallback takes precedence over global)\n if (state.isLoading) {\n return <>{loadingFallback ?? routingConfig.loadingFallback ?? null}</>;\n }\n\n // Access denied - redirect\n if (accessDeniedReason && redirectTarget) {\n // Show access denied fallback briefly before redirect (local or global)\n const fallback = accessDeniedFallback ?? routingConfig.accessDeniedFallback;\n if (fallback) {\n return <>{fallback}</>;\n }\n\n // Build final redirect URL with return path if needed\n const finalRedirect = buildRedirectUrl(\n redirectTarget,\n returnTo,\n location.pathname + location.search,\n routingConfig.returnToParam,\n routingConfig.returnToStorage\n );\n\n return <Navigate to={finalRedirect} replace />;\n }\n\n // Access granted\n return <>{children}</>;\n};\n\n// Convenience components\n\nexport const TenantZone: FC<Omit<ZoneRouteProps, 'tenant'>> = props => (\n <ZoneRoute tenant=\"required\" {...props} />\n);\n\nexport const PublicZone: FC<Omit<ZoneRouteProps, 'tenant'>> = props => (\n <ZoneRoute tenant=\"forbidden\" {...props} />\n);\n\nexport const AuthenticatedZone: FC<Omit<ZoneRouteProps, 'auth'>> = props => (\n <ZoneRoute auth=\"required\" {...props} />\n);\n\nexport const GuestZone: FC<Omit<ZoneRouteProps, 'auth'>> = props => (\n <ZoneRoute auth=\"forbidden\" {...props} />\n);\n\nexport const AdminZone: FC<Omit<ZoneRouteProps, 'auth' | 'userType'>> = props => (\n <ZoneRoute auth=\"required\" userType={UserType.TENANT_ADMIN} {...props} />\n);\n\nexport const UserZone: FC<Omit<ZoneRouteProps, 'auth' | 'userType'>> = props => (\n <ZoneRoute auth=\"required\" userType={UserType.USER} {...props} />\n);\n\nexport const OpenZone: FC<Omit<ZoneRouteProps, 'tenant' | 'auth'>> = props => (\n <ZoneRoute tenant=\"optional\" auth=\"optional\" {...props} />\n);\n\nexport const TenantAuthenticatedZone: FC<Omit<ZoneRouteProps, 'tenant' | 'auth'>> = props => (\n <ZoneRoute tenant=\"required\" auth=\"required\" {...props} />\n);\n\nexport const TenantOpenZone: FC<Omit<ZoneRouteProps, 'tenant' | 'auth'>> = props => (\n <ZoneRoute tenant=\"required\" auth=\"optional\" {...props} />\n);\n\nexport const TenantGuestZone: FC<Omit<ZoneRouteProps, 'tenant' | 'auth'>> = props => (\n <ZoneRoute tenant=\"required\" auth=\"forbidden\" {...props} />\n);\n\nexport default ZoneRoute;\n","import { ReactNode } from 'react';\nimport { useSubscription } from '../providers/SubscriptionProvider';\n\nexport interface SubscriptionGuardProps {\n children: ReactNode;\n fallback?: ReactNode;\n allowedPlans?: string[];\n requiredFeature?: string;\n}\n\nconst DefaultFallback = () => (\n <div\n style={{\n padding: '2rem',\n textAlign: 'center',\n backgroundColor: '#fef2f2',\n border: '1px solid #fecaca',\n borderRadius: '8px',\n color: '#dc2626',\n }}\n >\n <h3 style={{ margin: '0 0 1rem 0' }}>🔒 Subscription Required</h3>\n <p style={{ margin: 0 }}>\n This feature requires a higher subscription plan. Please upgrade your plan to access this\n content.\n </p>\n </div>\n);\n\nexport function SubscriptionGuard({\n children,\n fallback = <DefaultFallback />,\n allowedPlans,\n requiredFeature,\n}: SubscriptionGuardProps) {\n const { subscription, hasAllowedPlan, isFeatureEnabled, loading } = useSubscription();\n\n // Show loading state\n if (loading) {\n return (\n <div\n style={{\n padding: '2rem',\n textAlign: 'center',\n color: '#6b7280',\n }}\n >\n Loading subscription...\n </div>\n );\n }\n\n // No subscription data available\n if (!subscription) {\n return <>{fallback}</>;\n }\n\n // Check if subscription is active\n if (!subscription.isActive) {\n return <>{fallback}</>;\n }\n\n // Check allowed plans requirement\n if (allowedPlans && allowedPlans.length > 0 && !hasAllowedPlan(allowedPlans)) {\n return <>{fallback}</>;\n }\n\n // Check required feature\n if (requiredFeature && !isFeatureEnabled(requiredFeature)) {\n return <>{fallback}</>;\n }\n\n // All checks passed, render children\n return <>{children}</>;\n}\n","import { ReactNode } from 'react';\nimport { useFeatureFlags } from '../providers/FeatureFlagProvider';\n\ninterface FeatureFlagProps {\n name: string;\n children: ReactNode;\n fallback?: ReactNode;\n}\n\nconst DefaultFallback = ({ flagName }: { flagName: string }) => (\n <div\n style={{\n display: 'flex',\n flexDirection: 'column',\n justifyContent: 'center',\n alignItems: 'center',\n padding: '15px',\n backgroundColor: '#f8f9fa',\n border: '1px solid #dee2e6',\n borderRadius: '6px',\n textAlign: 'center',\n fontFamily: 'system-ui, sans-serif',\n color: '#6c757d',\n }}\n >\n <div style={{ fontSize: '24px', marginBottom: '8px' }}>🚧</div>\n <div style={{ fontSize: '14px', fontWeight: '500', marginBottom: '4px' }}>\n Feature Not Available\n </div>\n <div style={{ fontSize: '12px', opacity: 0.7 }}>Feature flag \"{flagName}\" is disabled</div>\n </div>\n);\n\nexport function FeatureFlag({ name, children, fallback }: FeatureFlagProps) {\n const { isEnabled, loading } = useFeatureFlags();\n\n // Show loading state while fetching feature flags\n if (loading) {\n return (\n <div\n style={{\n display: 'flex',\n justifyContent: 'center',\n alignItems: 'center',\n padding: '10px',\n color: '#6c757d',\n fontSize: '14px',\n }}\n >\n Loading feature flags...\n </div>\n );\n }\n\n // Show children if feature flag is enabled\n if (isEnabled(name)) {\n return <>{children}</>;\n }\n\n // Show fallback if provided, otherwise show default fallback\n return <>{fallback || <DefaultFallback flagName={name} />}</>;\n}\n","import React, { useState } from 'react';\nimport { useAuth } from '../providers/AuthProvider';\nimport { useTenantInfo } from '../providers/TenantProvider';\n\nexport interface LoginFormCopy {\n title?: string;\n usernameLabel?: string;\n usernamePlaceholder?: string;\n passwordLabel?: string;\n passwordPlaceholder?: string;\n submitButton?: string;\n forgotPasswordLink?: string;\n signupLink?: string;\n signupText?: string;\n magicLinkText?: string;\n magicLinkLink?: string;\n errorMessage?: string;\n loadingText?: string;\n tenantNotFoundError?: string;\n dividerBullet?: string;\n showPasswordAriaLabel?: string;\n hidePasswordAriaLabel?: string;\n}\n\nexport interface LoginFormStyles {\n container?: React.CSSProperties;\n title?: React.CSSProperties;\n form?: React.CSSProperties;\n fieldGroup?: React.CSSProperties;\n label?: React.CSSProperties;\n input?: React.CSSProperties;\n inputError?: React.CSSProperties;\n inputContainer?: React.CSSProperties;\n passwordToggle?: React.CSSProperties;\n button?: React.CSSProperties;\n buttonDisabled?: React.CSSProperties;\n buttonLoading?: React.CSSProperties;\n errorText?: React.CSSProperties;\n linkContainer?: React.CSSProperties;\n link?: React.CSSProperties;\n divider?: React.CSSProperties;\n inputWithIcon?: React.CSSProperties;\n}\n\nexport interface LoginFormIcons {\n showPassword?: React.ReactNode;\n hidePassword?: React.ReactNode;\n}\n\nexport interface LoginFormProps {\n copy?: LoginFormCopy;\n styles?: LoginFormStyles;\n icons?: LoginFormIcons;\n onSuccess?: (data: any) => void;\n onError?: (error: string) => void;\n onForgotPassword?: () => void;\n onSignupClick?: () => void;\n onMagicLinkClick?: () => void;\n showForgotPassword?: boolean;\n showSignupLink?: boolean;\n showMagicLinkOption?: boolean;\n className?: string;\n}\n\n// Default SVG icons for password toggle\nconst EyeIcon = () => (\n <svg\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n style={{ flexShrink: 0 }}\n >\n <path d=\"M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z\" />\n <circle cx=\"12\" cy=\"12\" r=\"3\" />\n </svg>\n);\n\nconst EyeOffIcon = () => (\n <svg\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n style={{ flexShrink: 0 }}\n >\n <path d=\"M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24\" />\n <line x1=\"1\" y1=\"1\" x2=\"23\" y2=\"23\" />\n </svg>\n);\n\nconst defaultIcons: Required<LoginFormIcons> = {\n showPassword: <EyeIcon />,\n hidePassword: <EyeOffIcon />,\n};\n\nconst defaultCopy: Required<LoginFormCopy> = {\n title: 'Sign In',\n usernameLabel: 'Email or Phone',\n usernamePlaceholder: 'Enter your email or phone number',\n passwordLabel: 'Password',\n passwordPlaceholder: 'Enter your password',\n submitButton: 'Sign In',\n forgotPasswordLink: 'Forgot your password?',\n signupLink: 'Sign up here',\n signupText: \"Don't have an account?\",\n magicLinkText: 'Prefer passwordless?',\n magicLinkLink: 'Use Magic Link',\n errorMessage: 'Invalid credentials',\n loadingText: 'Signing in...',\n tenantNotFoundError: 'Tenant not found',\n dividerBullet: '•',\n showPasswordAriaLabel: 'Show password',\n hidePasswordAriaLabel: 'Hide password',\n};\n\nconst defaultStyles: Required<LoginFormStyles> = {\n container: {\n maxWidth: '400px',\n width: '100%',\n margin: '0 auto',\n padding: '2rem',\n backgroundColor: '#ffffff',\n borderRadius: '8px',\n boxShadow: '0 2px 10px rgba(0, 0, 0, 0.1)',\n },\n title: {\n fontSize: '1.5rem',\n fontWeight: 'bold',\n textAlign: 'center',\n marginBottom: '1.5rem',\n color: '#333333',\n },\n form: {\n display: 'flex',\n flexDirection: 'column',\n gap: '1rem',\n },\n fieldGroup: {\n display: 'flex',\n flexDirection: 'column',\n gap: '0.5rem',\n },\n label: {\n fontSize: '0.875rem',\n fontWeight: '500',\n color: '#374151',\n },\n input: {\n padding: '0.75rem',\n border: '1px solid #d1d5db',\n borderRadius: '6px',\n fontSize: '1rem',\n transition: 'border-color 0.15s ease-in-out',\n outline: 'none',\n width: '100%',\n },\n inputError: {\n borderColor: '#ef4444',\n boxShadow: '0 0 0 3px rgba(239, 68, 68, 0.1)',\n },\n inputContainer: {\n position: 'relative',\n display: 'flex',\n alignItems: 'center',\n },\n passwordToggle: {\n position: 'absolute',\n right: '0.75rem',\n background: 'none',\n border: 'none',\n cursor: 'pointer',\n padding: '0.25rem',\n color: '#6b7280',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n width: '24px',\n height: '24px',\n borderRadius: '4px',\n transition: 'background-color 0.15s ease-in-out',\n },\n button: {\n padding: '0.75rem 1rem',\n backgroundColor: '#3b82f6',\n color: 'white',\n border: 'none',\n borderRadius: '6px',\n fontSize: '1rem',\n fontWeight: '500',\n cursor: 'pointer',\n transition: 'background-color 0.15s ease-in-out',\n marginTop: '0.5rem',\n },\n buttonDisabled: {\n backgroundColor: '#9ca3af',\n cursor: 'not-allowed',\n },\n buttonLoading: {\n backgroundColor: '#6b7280',\n },\n errorText: {\n color: '#ef4444',\n fontSize: '0.875rem',\n textAlign: 'center',\n marginTop: '0.5rem',\n },\n linkContainer: {\n textAlign: 'center',\n marginTop: '1rem',\n },\n link: {\n color: '#3b82f6',\n textDecoration: 'none',\n fontSize: '0.875rem',\n cursor: 'pointer',\n },\n divider: {\n margin: '0.5rem 0',\n color: '#6b7280',\n fontSize: '0.875rem',\n },\n inputWithIcon: {\n paddingRight: '2.5rem',\n },\n};\n\nexport function LoginForm({\n copy = {},\n styles = {},\n icons = {},\n onSuccess,\n onError,\n onForgotPassword,\n onSignupClick,\n onMagicLinkClick,\n showForgotPassword = true,\n showSignupLink = true,\n showMagicLinkOption = true,\n className,\n}: LoginFormProps) {\n const [username, setUsername] = useState('');\n const [password, setPassword] = useState('');\n const [showPassword, setShowPassword] = useState(false);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState('');\n const [fieldErrors, setFieldErrors] = useState<{ username?: boolean; password?: boolean }>({});\n\n const { login } = useAuth();\n const { tenant } = useTenantInfo();\n\n const mergedCopy = { ...defaultCopy, ...copy };\n const mergedStyles = { ...defaultStyles, ...styles };\n const mergedIcons = { ...defaultIcons, ...icons };\n\n const validateForm = () => {\n const errors: { username?: boolean; password?: boolean } = {};\n\n if (!username.trim()) errors.username = true;\n if (!password.trim()) errors.password = true;\n\n setFieldErrors(errors);\n return Object.keys(errors).length === 0;\n };\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n\n if (!validateForm()) return;\n if (!tenant?.id) {\n setError(mergedCopy.tenantNotFoundError);\n return;\n }\n\n setLoading(true);\n setError('');\n\n try {\n const result = await login({\n username,\n password,\n // tenantId inferred from context automatically\n });\n onSuccess?.(result);\n } catch (err: any) {\n const errorMessage = err.message || mergedCopy.errorMessage;\n setError(errorMessage);\n onError?.(errorMessage);\n } finally {\n setLoading(false);\n }\n };\n\n const getInputStyle = (field: 'username' | 'password') => ({\n ...mergedStyles.input,\n ...(fieldErrors[field] ? mergedStyles.inputError : {}),\n });\n\n const getButtonStyle = () => ({\n ...mergedStyles.button,\n ...(loading ? mergedStyles.buttonLoading : {}),\n ...(!username || !password || loading ? mergedStyles.buttonDisabled : {}),\n });\n\n return (\n <div className={className} style={mergedStyles.container}>\n <h2 style={mergedStyles.title}>{mergedCopy.title}</h2>\n\n <form onSubmit={handleSubmit} style={mergedStyles.form}>\n <div style={mergedStyles.fieldGroup}>\n <label style={mergedStyles.label}>{mergedCopy.usernameLabel}</label>\n <input\n id=\"username\"\n name=\"username\"\n type=\"text\"\n value={username}\n onChange={e => {\n setUsername(e.target.value);\n if (fieldErrors.username) {\n setFieldErrors(prev => ({ ...prev, username: false }));\n }\n }}\n placeholder={mergedCopy.usernamePlaceholder}\n style={getInputStyle('username')}\n disabled={loading}\n />\n </div>\n\n <div style={mergedStyles.fieldGroup}>\n <label style={mergedStyles.label}>{mergedCopy.passwordLabel}</label>\n <div style={mergedStyles.inputContainer}>\n <input\n id=\"password\"\n name=\"password\"\n type={showPassword ? 'text' : 'password'}\n value={password}\n onChange={e => {\n setPassword(e.target.value);\n if (fieldErrors.password) {\n setFieldErrors(prev => ({ ...prev, password: false }));\n }\n }}\n placeholder={mergedCopy.passwordPlaceholder}\n style={{\n ...getInputStyle('password'),\n ...mergedStyles.inputWithIcon,\n }}\n disabled={loading}\n />\n <button\n type=\"button\"\n onClick={() => setShowPassword(!showPassword)}\n style={mergedStyles.passwordToggle}\n disabled={loading}\n aria-label={\n showPassword ? mergedCopy.hidePasswordAriaLabel : mergedCopy.showPasswordAriaLabel\n }\n >\n {showPassword ? mergedIcons.hidePassword : mergedIcons.showPassword}\n </button>\n </div>\n </div>\n\n <button type=\"submit\" disabled={!username || !password || loading} style={getButtonStyle()}>\n {loading ? mergedCopy.loadingText : mergedCopy.submitButton}\n </button>\n\n {error && <div style={mergedStyles.errorText}>{error}</div>}\n </form>\n\n {(showForgotPassword || showSignupLink || showMagicLinkOption) && (\n <div style={mergedStyles.linkContainer}>\n {showMagicLinkOption && (\n <div>\n <span style={mergedStyles.divider}>{mergedCopy.magicLinkText} </span>\n <a onClick={onMagicLinkClick} style={mergedStyles.link}>\n {mergedCopy.magicLinkLink}\n </a>\n </div>\n )}\n\n {showMagicLinkOption && (showForgotPassword || showSignupLink) && (\n <div style={mergedStyles.divider}>{mergedCopy.dividerBullet}</div>\n )}\n\n {showForgotPassword && (\n <a onClick={onForgotPassword} style={mergedStyles.link}>\n {mergedCopy.forgotPasswordLink}\n </a>\n )}\n\n {showForgotPassword && showSignupLink && (\n <div style={mergedStyles.divider}>{mergedCopy.dividerBullet}</div>\n )}\n\n {showSignupLink && (\n <div>\n <span style={mergedStyles.divider}>{mergedCopy.signupText} </span>\n <a onClick={onSignupClick} style={mergedStyles.link}>\n {mergedCopy.signupLink}\n </a>\n </div>\n )}\n </div>\n )}\n </div>\n );\n}\n","import React, { useState } from 'react';\nimport { useAuth } from '../providers/AuthProvider';\nimport { useTenantInfo } from '../providers/TenantProvider';\n\nexport interface SignupFormCopy {\n title?: string;\n nameLabel?: string;\n namePlaceholder?: string;\n lastNameLabel?: string;\n lastNamePlaceholder?: string;\n emailLabel?: string;\n emailPlaceholder?: string;\n phoneNumberLabel?: string;\n phoneNumberPlaceholder?: string;\n passwordLabel?: string;\n passwordPlaceholder?: string;\n confirmPasswordLabel?: string;\n confirmPasswordPlaceholder?: string;\n tenantNameLabel?: string;\n tenantNamePlaceholder?: string;\n submitButton?: string;\n loginLink?: string;\n loginText?: string;\n magicLinkText?: string;\n magicLinkLink?: string;\n errorMessage?: string;\n loadingText?: string;\n passwordMismatchError?: string;\n isAdminLabel?: string;\n isAdminDescription?: string;\n contactMethodHint?: string;\n tenantNotFoundError?: string;\n dividerBullet?: string;\n}\n\nexport interface SignupFormStyles {\n container?: React.CSSProperties;\n title?: React.CSSProperties;\n form?: React.CSSProperties;\n fieldGroup?: React.CSSProperties;\n label?: React.CSSProperties;\n input?: React.CSSProperties;\n inputError?: React.CSSProperties;\n checkbox?: React.CSSProperties;\n checkboxContainer?: React.CSSProperties;\n checkboxLabel?: React.CSSProperties;\n button?: React.CSSProperties;\n buttonDisabled?: React.CSSProperties;\n buttonLoading?: React.CSSProperties;\n errorText?: React.CSSProperties;\n linkContainer?: React.CSSProperties;\n link?: React.CSSProperties;\n divider?: React.CSSProperties;\n hintText?: React.CSSProperties;\n}\n\nexport type SignupType = 'user' | 'tenant';\n\nexport interface SignupFormProps {\n copy?: SignupFormCopy;\n styles?: SignupFormStyles;\n signupType?: SignupType;\n onSuccess?: (data: any) => void;\n onError?: (error: string) => void;\n onLoginClick?: () => void;\n onMagicLinkClick?: () => void;\n showLoginLink?: boolean;\n showMagicLinkOption?: boolean;\n className?: string;\n}\n\nconst defaultCopy: Required<SignupFormCopy> = {\n title: 'Create Account',\n nameLabel: 'First Name',\n namePlaceholder: 'Enter your first name',\n lastNameLabel: 'Last Name',\n lastNamePlaceholder: 'Enter your last name',\n emailLabel: 'Email',\n emailPlaceholder: 'Enter your email',\n phoneNumberLabel: 'Phone Number',\n phoneNumberPlaceholder: 'Enter your phone number',\n passwordLabel: 'Password',\n passwordPlaceholder: 'Enter your password',\n confirmPasswordLabel: 'Confirm Password',\n confirmPasswordPlaceholder: 'Confirm your password',\n tenantNameLabel: 'Organization Name',\n tenantNamePlaceholder: 'Enter your organization name',\n submitButton: 'Create Account',\n loginLink: 'Sign in here',\n loginText: 'Already have an account?',\n magicLinkText: 'Prefer passwordless?',\n magicLinkLink: 'Use Magic Link',\n errorMessage: 'Failed to create account',\n loadingText: 'Creating account...',\n passwordMismatchError: 'Passwords do not match',\n isAdminLabel: 'Create new organization',\n isAdminDescription: 'Check this if you want to create a new organization',\n contactMethodHint: 'At least one contact method (email or phone) is required',\n tenantNotFoundError: 'Tenant not found',\n dividerBullet: '•',\n};\n\nconst defaultStyles: Required<SignupFormStyles> = {\n container: {\n maxWidth: '400px',\n width: '100%',\n margin: '0 auto',\n padding: '2rem',\n backgroundColor: '#ffffff',\n borderRadius: '8px',\n boxShadow: '0 2px 10px rgba(0, 0, 0, 0.1)',\n },\n title: {\n fontSize: '1.5rem',\n fontWeight: 'bold',\n textAlign: 'center',\n marginBottom: '1.5rem',\n color: '#333333',\n },\n form: {\n display: 'flex',\n flexDirection: 'column',\n gap: '1rem',\n },\n fieldGroup: {\n display: 'flex',\n flexDirection: 'column',\n gap: '0.5rem',\n },\n label: {\n fontSize: '0.875rem',\n fontWeight: '500',\n color: '#374151',\n },\n input: {\n padding: '0.75rem',\n border: '1px solid #d1d5db',\n borderRadius: '6px',\n fontSize: '1rem',\n transition: 'border-color 0.15s ease-in-out',\n outline: 'none',\n },\n inputError: {\n borderColor: '#ef4444',\n boxShadow: '0 0 0 3px rgba(239, 68, 68, 0.1)',\n },\n checkbox: {\n marginRight: '0.5rem',\n },\n checkboxContainer: {\n display: 'flex',\n alignItems: 'flex-start',\n gap: '0.5rem',\n padding: '0.5rem 0',\n },\n checkboxLabel: {\n fontSize: '0.875rem',\n color: '#374151',\n lineHeight: '1.4',\n },\n button: {\n padding: '0.75rem 1rem',\n backgroundColor: '#10b981',\n color: 'white',\n border: 'none',\n borderRadius: '6px',\n fontSize: '1rem',\n fontWeight: '500',\n cursor: 'pointer',\n transition: 'background-color 0.15s ease-in-out',\n marginTop: '0.5rem',\n },\n buttonDisabled: {\n backgroundColor: '#9ca3af',\n cursor: 'not-allowed',\n },\n buttonLoading: {\n backgroundColor: '#6b7280',\n },\n errorText: {\n color: '#ef4444',\n fontSize: '0.875rem',\n textAlign: 'center',\n marginTop: '0.5rem',\n },\n linkContainer: {\n textAlign: 'center',\n marginTop: '1rem',\n },\n link: {\n color: '#3b82f6',\n textDecoration: 'none',\n fontSize: '0.875rem',\n cursor: 'pointer',\n },\n divider: {\n margin: '0.5rem 0',\n color: '#6b7280',\n fontSize: '0.875rem',\n },\n hintText: {\n fontSize: '0.875rem',\n color: '#6b7280',\n textAlign: 'center',\n margin: '0.5rem 0',\n },\n};\n\nexport function SignupForm({\n copy = {},\n styles = {},\n signupType = 'user',\n onSuccess,\n onError,\n onLoginClick,\n onMagicLinkClick,\n showLoginLink = true,\n showMagicLinkOption = true,\n className,\n}: SignupFormProps) {\n const [name, setName] = useState('');\n const [lastName, setLastName] = useState('');\n const [email, setEmail] = useState('');\n const [phoneNumber, setPhoneNumber] = useState('');\n const [password, setPassword] = useState('');\n const [confirmPassword, setConfirmPassword] = useState('');\n const [tenantName, setTenantName] = useState('');\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState('');\n const [fieldErrors, setFieldErrors] = useState<{\n name?: boolean;\n email?: boolean;\n phoneNumber?: boolean;\n password?: boolean;\n confirmPassword?: boolean;\n tenantName?: boolean;\n }>({});\n\n const { signup, signupTenantAdmin } = useAuth();\n const { tenant } = useTenantInfo();\n\n const mergedCopy = { ...defaultCopy, ...copy };\n const mergedStyles = { ...defaultStyles, ...styles };\n\n const validateForm = () => {\n const errors: {\n name?: boolean;\n email?: boolean;\n phoneNumber?: boolean;\n password?: boolean;\n confirmPassword?: boolean;\n tenantName?: boolean;\n } = {};\n\n if (!name.trim()) errors.name = true;\n if (!email.trim() && !phoneNumber.trim()) {\n errors.email = true;\n errors.phoneNumber = true;\n }\n if (!password.trim()) errors.password = true;\n if (!confirmPassword.trim()) errors.confirmPassword = true;\n if (signupType === 'tenant' && !tenantName.trim()) errors.tenantName = true;\n\n setFieldErrors(errors);\n return Object.keys(errors).length === 0;\n };\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n\n if (!validateForm()) return;\n\n if (password !== confirmPassword) {\n setError(mergedCopy.passwordMismatchError);\n setFieldErrors({ confirmPassword: true });\n return;\n }\n\n if (signupType === 'user' && !tenant?.id) {\n setError(mergedCopy.tenantNotFoundError);\n return;\n }\n\n setLoading(true);\n setError('');\n\n try {\n let result;\n if (signupType === 'tenant') {\n result = await signupTenantAdmin({\n email: email || undefined,\n phoneNumber: phoneNumber || undefined,\n name,\n password,\n tenantName,\n lastName: lastName || undefined,\n });\n } else {\n result = await signup({\n email: email || undefined,\n phoneNumber: phoneNumber || undefined,\n name,\n password,\n tenantId: tenant!.id,\n lastName: lastName || undefined,\n });\n }\n onSuccess?.(result);\n } catch (err: any) {\n const errorMessage = err.message || mergedCopy.errorMessage;\n setError(errorMessage);\n onError?.(errorMessage);\n } finally {\n setLoading(false);\n }\n };\n\n const getInputStyle = (field: keyof typeof fieldErrors) => ({\n ...mergedStyles.input,\n ...(fieldErrors[field] ? mergedStyles.inputError : {}),\n });\n\n const getButtonStyle = () => ({\n ...mergedStyles.button,\n ...(loading ? mergedStyles.buttonLoading : {}),\n ...(!name ||\n (!email && !phoneNumber) ||\n !password ||\n !confirmPassword ||\n loading ||\n (signupType === 'tenant' && !tenantName)\n ? mergedStyles.buttonDisabled\n : {}),\n });\n\n const isFormValid =\n name &&\n (email || phoneNumber) &&\n password &&\n confirmPassword &&\n (signupType === 'user' || tenantName);\n\n return (\n <div className={className} style={mergedStyles.container}>\n <h2 style={mergedStyles.title}>{mergedCopy.title}</h2>\n\n <form onSubmit={handleSubmit} style={mergedStyles.form}>\n <div style={mergedStyles.fieldGroup}>\n <label style={mergedStyles.label}>{mergedCopy.nameLabel}</label>\n <input\n id=\"name\"\n name=\"name\"\n type=\"text\"\n value={name}\n onChange={e => {\n setName(e.target.value);\n if (fieldErrors.name) {\n setFieldErrors(prev => ({ ...prev, name: false }));\n }\n }}\n placeholder={mergedCopy.namePlaceholder}\n style={getInputStyle('name')}\n disabled={loading}\n />\n </div>\n\n <div style={mergedStyles.fieldGroup}>\n <label style={mergedStyles.label}>{mergedCopy.lastNameLabel}</label>\n <input\n id=\"lastName\"\n name=\"lastName\"\n type=\"text\"\n value={lastName}\n onChange={e => setLastName(e.target.value)}\n placeholder={mergedCopy.lastNamePlaceholder}\n style={mergedStyles.input}\n disabled={loading}\n />\n </div>\n\n <div style={mergedStyles.fieldGroup}>\n <label style={mergedStyles.label}>{mergedCopy.emailLabel}</label>\n <input\n id=\"email\"\n name=\"email\"\n type=\"email\"\n value={email}\n onChange={e => {\n setEmail(e.target.value);\n if (fieldErrors.email) {\n setFieldErrors(prev => ({ ...prev, email: false, phoneNumber: false }));\n }\n }}\n placeholder={mergedCopy.emailPlaceholder}\n style={getInputStyle('email')}\n disabled={loading}\n />\n </div>\n\n <div style={mergedStyles.fieldGroup}>\n <label style={mergedStyles.label}>{mergedCopy.phoneNumberLabel}</label>\n <input\n id=\"phoneNumber\"\n name=\"phoneNumber\"\n type=\"tel\"\n value={phoneNumber}\n onChange={e => {\n setPhoneNumber(e.target.value);\n if (fieldErrors.phoneNumber) {\n setFieldErrors(prev => ({ ...prev, email: false, phoneNumber: false }));\n }\n }}\n placeholder={mergedCopy.phoneNumberPlaceholder}\n style={getInputStyle('phoneNumber')}\n disabled={loading}\n />\n </div>\n\n <div style={mergedStyles.hintText}>{mergedCopy.contactMethodHint}</div>\n\n <div style={mergedStyles.fieldGroup}>\n <label style={mergedStyles.label}>{mergedCopy.passwordLabel}</label>\n <input\n id=\"password\"\n name=\"password\"\n type=\"password\"\n value={password}\n onChange={e => {\n setPassword(e.target.value);\n if (fieldErrors.password) {\n setFieldErrors(prev => ({ ...prev, password: false }));\n }\n }}\n placeholder={mergedCopy.passwordPlaceholder}\n style={getInputStyle('password')}\n disabled={loading}\n />\n </div>\n\n <div style={mergedStyles.fieldGroup}>\n <label style={mergedStyles.label}>{mergedCopy.confirmPasswordLabel}</label>\n <input\n id=\"confirmPassword\"\n name=\"confirmPassword\"\n type=\"password\"\n value={confirmPassword}\n onChange={e => {\n setConfirmPassword(e.target.value);\n if (fieldErrors.confirmPassword) {\n setFieldErrors(prev => ({ ...prev, confirmPassword: false }));\n }\n if (error === mergedCopy.passwordMismatchError) {\n setError('');\n }\n }}\n placeholder={mergedCopy.confirmPasswordPlaceholder}\n style={getInputStyle('confirmPassword')}\n disabled={loading}\n />\n </div>\n\n {signupType === 'tenant' && (\n <div style={mergedStyles.fieldGroup}>\n <label style={mergedStyles.label}>{mergedCopy.tenantNameLabel}</label>\n <input\n id=\"tenantName\"\n name=\"tenantName\"\n type=\"text\"\n value={tenantName}\n onChange={e => {\n setTenantName(e.target.value);\n if (fieldErrors.tenantName) {\n setFieldErrors(prev => ({ ...prev, tenantName: false }));\n }\n }}\n placeholder={mergedCopy.tenantNamePlaceholder}\n style={getInputStyle('tenantName')}\n disabled={loading}\n />\n </div>\n )}\n\n <button type=\"submit\" disabled={!isFormValid || loading} style={getButtonStyle()}>\n {loading ? mergedCopy.loadingText : mergedCopy.submitButton}\n </button>\n\n {error && <div style={mergedStyles.errorText}>{error}</div>}\n </form>\n\n {(showLoginLink || showMagicLinkOption) && (\n <div style={mergedStyles.linkContainer}>\n {showMagicLinkOption && (\n <div>\n <span style={mergedStyles.divider}>{mergedCopy.magicLinkText} </span>\n <a onClick={onMagicLinkClick} style={mergedStyles.link}>\n {mergedCopy.magicLinkLink}\n </a>\n </div>\n )}\n\n {showMagicLinkOption && showLoginLink && (\n <div style={mergedStyles.divider}>{mergedCopy.dividerBullet}</div>\n )}\n\n {showLoginLink && (\n <div>\n <span style={mergedStyles.divider}>{mergedCopy.loginText} </span>\n <a onClick={onLoginClick} style={mergedStyles.link}>\n {mergedCopy.loginLink}\n </a>\n </div>\n )}\n </div>\n )}\n </div>\n );\n}\n","import React, { useState, useEffect } from 'react';\nimport { useAuth } from '../providers/AuthProvider';\nimport { useTenantInfo } from '../providers/TenantProvider';\n\nexport interface MagicLinkFormCopy {\n title?: string;\n emailLabel?: string;\n emailPlaceholder?: string;\n nameLabel?: string;\n namePlaceholder?: string;\n lastNameLabel?: string;\n lastNamePlaceholder?: string;\n submitButton?: string;\n loginLink?: string;\n signupLink?: string;\n loginText?: string;\n signupText?: string;\n successMessage?: string;\n errorMessage?: string;\n loadingText?: string;\n verifyingText?: string;\n verifyingDescription?: string;\n description?: string;\n showNameToggle?: string;\n hideNameToggle?: string;\n tenantNotFoundError?: string;\n missingTenantOrEmailError?: string;\n dividerBullet?: string;\n}\n\nexport interface MagicLinkFormStyles {\n container?: React.CSSProperties;\n title?: React.CSSProperties;\n description?: React.CSSProperties;\n form?: React.CSSProperties;\n fieldGroup?: React.CSSProperties;\n label?: React.CSSProperties;\n input?: React.CSSProperties;\n inputError?: React.CSSProperties;\n button?: React.CSSProperties;\n buttonDisabled?: React.CSSProperties;\n buttonLoading?: React.CSSProperties;\n errorText?: React.CSSProperties;\n successText?: React.CSSProperties;\n linkContainer?: React.CSSProperties;\n link?: React.CSSProperties;\n divider?: React.CSSProperties;\n verifyingContainer?: React.CSSProperties;\n verifyingText?: React.CSSProperties;\n toggleContainer?: React.CSSProperties;\n toggleLink?: React.CSSProperties;\n}\n\nexport interface MagicLinkFormProps {\n copy?: MagicLinkFormCopy;\n styles?: MagicLinkFormStyles;\n onSuccess?: (data: any) => void;\n onError?: (error: string) => void;\n onLoginClick?: () => void;\n onSignupClick?: () => void;\n showTraditionalLinks?: boolean;\n className?: string;\n // Auto-verify magic link if token is provided (e.g., from URL params)\n verifyToken?: string;\n // Frontend URL for magic link callback (if not provided, will use window.location.origin)\n frontendUrl?: string;\n}\n\nconst defaultCopy: Required<MagicLinkFormCopy> = {\n title: 'Sign In with Magic Link',\n emailLabel: 'Email',\n emailPlaceholder: 'Enter your email',\n nameLabel: 'Name',\n namePlaceholder: 'Enter your name',\n lastNameLabel: 'Last Name',\n lastNamePlaceholder: 'Enter your last name',\n submitButton: 'Send Magic Link',\n loginLink: 'Sign in with password',\n signupLink: 'Sign up with password',\n loginText: 'Already have an account?',\n signupText: 'Prefer traditional signup?',\n successMessage: 'Magic link sent! Check your email and click the link to sign in.',\n errorMessage: 'Failed to send magic link. Please try again.',\n loadingText: 'Sending magic link...',\n verifyingText: 'Verifying magic link...',\n verifyingDescription: 'Please wait while we verify your magic link...',\n description:\n \"Enter your email to receive a magic link. If you don't have an account, we'll create one for you.\",\n showNameToggle: 'New user? Add your name',\n hideNameToggle: 'Existing user? Hide name fields',\n tenantNotFoundError: 'Tenant not found',\n missingTenantOrEmailError: 'Missing tenant or email',\n dividerBullet: '•',\n};\n\nconst defaultStyles: Required<MagicLinkFormStyles> = {\n container: {\n maxWidth: '400px',\n width: '100%',\n margin: '0 auto',\n padding: '2rem',\n backgroundColor: '#ffffff',\n borderRadius: '8px',\n boxShadow: '0 2px 10px rgba(0, 0, 0, 0.1)',\n },\n title: {\n fontSize: '1.5rem',\n fontWeight: 'bold',\n textAlign: 'center',\n marginBottom: '1rem',\n color: '#333333',\n },\n description: {\n fontSize: '0.875rem',\n color: '#6b7280',\n textAlign: 'center',\n marginBottom: '1.5rem',\n lineHeight: '1.5',\n },\n form: {\n display: 'flex',\n flexDirection: 'column',\n gap: '1rem',\n },\n fieldGroup: {\n display: 'flex',\n flexDirection: 'column',\n gap: '0.5rem',\n },\n label: {\n fontSize: '0.875rem',\n fontWeight: '500',\n color: '#374151',\n },\n input: {\n padding: '0.75rem',\n border: '1px solid #d1d5db',\n borderRadius: '6px',\n fontSize: '1rem',\n transition: 'border-color 0.15s ease-in-out',\n outline: 'none',\n width: '100%',\n },\n inputError: {\n borderColor: '#ef4444',\n boxShadow: '0 0 0 3px rgba(239, 68, 68, 0.1)',\n },\n button: {\n padding: '0.75rem 1rem',\n backgroundColor: '#3b82f6',\n color: 'white',\n border: 'none',\n borderRadius: '6px',\n fontSize: '1rem',\n fontWeight: '500',\n cursor: 'pointer',\n transition: 'background-color 0.15s ease-in-out',\n marginTop: '0.5rem',\n },\n buttonDisabled: {\n backgroundColor: '#9ca3af',\n cursor: 'not-allowed',\n },\n buttonLoading: {\n backgroundColor: '#6b7280',\n },\n errorText: {\n color: '#ef4444',\n fontSize: '0.875rem',\n textAlign: 'center',\n marginTop: '0.5rem',\n },\n successText: {\n color: '#10b981',\n fontSize: '0.875rem',\n textAlign: 'center',\n marginTop: '0.5rem',\n padding: '0.75rem',\n backgroundColor: '#f0fdf4',\n borderRadius: '6px',\n border: '1px solid #bbf7d0',\n },\n linkContainer: {\n textAlign: 'center',\n marginTop: '1rem',\n },\n link: {\n color: '#3b82f6',\n textDecoration: 'none',\n fontSize: '0.875rem',\n cursor: 'pointer',\n },\n divider: {\n margin: '0.5rem 0',\n color: '#6b7280',\n fontSize: '0.875rem',\n },\n verifyingContainer: {\n textAlign: 'center',\n padding: '2rem',\n },\n verifyingText: {\n fontSize: '1rem',\n color: '#6b7280',\n },\n toggleContainer: {\n textAlign: 'center',\n marginTop: '0.5rem',\n },\n toggleLink: {\n background: 'none',\n border: 'none',\n color: '#3b82f6',\n fontSize: '0.875rem',\n cursor: 'pointer',\n textDecoration: 'underline',\n },\n};\n\nexport function MagicLinkForm({\n copy = {},\n styles = {},\n onSuccess,\n onError,\n onLoginClick,\n onSignupClick,\n showTraditionalLinks = true,\n className,\n verifyToken,\n frontendUrl,\n}: MagicLinkFormProps) {\n const [email, setEmail] = useState('');\n const [name, setName] = useState('');\n const [lastName, setLastName] = useState('');\n const [loading, setLoading] = useState(false);\n const [verifying, setVerifying] = useState(false);\n const [error, setError] = useState('');\n const [success, setSuccess] = useState('');\n const [fieldErrors, setFieldErrors] = useState<{ email?: boolean; name?: boolean }>({});\n const [showNameFields, setShowNameFields] = useState(false);\n\n const { sendMagicLink, verifyMagicLink } = useAuth();\n const { tenant } = useTenantInfo();\n\n const mergedCopy = { ...defaultCopy, ...copy };\n const mergedStyles = { ...defaultStyles, ...styles };\n\n // Auto-verify magic link if token is provided\n useEffect(() => {\n if (verifyToken) {\n handleVerifyMagicLink(verifyToken);\n }\n }, [verifyToken]);\n\n const handleVerifyMagicLink = async (token: string) => {\n if (!tenant || !email) {\n setError(mergedCopy.missingTenantOrEmailError);\n return;\n }\n\n setVerifying(true);\n setError('');\n\n try {\n const result = await verifyMagicLink({\n token,\n email,\n // tenantId inferred from context automatically\n });\n onSuccess?.(result);\n } catch (err: any) {\n const errorMessage = err.message || 'Failed to verify magic link';\n setError(errorMessage);\n onError?.(errorMessage);\n } finally {\n setVerifying(false);\n }\n };\n\n const validateForm = () => {\n const errors: { email?: boolean; name?: boolean } = {};\n\n if (!email.trim()) errors.email = true;\n if (showNameFields && !name.trim()) errors.name = true;\n\n setFieldErrors(errors);\n return Object.keys(errors).length === 0;\n };\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n\n if (!validateForm()) return;\n if (!tenant?.id) {\n setError(mergedCopy.tenantNotFoundError);\n return;\n }\n\n setLoading(true);\n setError('');\n setSuccess('');\n\n try {\n const finalFrontendUrl =\n frontendUrl || (typeof window !== 'undefined' ? window.location.origin : '');\n const result = await sendMagicLink({\n email,\n tenantId: tenant.id,\n frontendUrl: finalFrontendUrl,\n name: showNameFields ? name : undefined,\n lastName: showNameFields ? lastName : undefined,\n });\n setSuccess(mergedCopy.successMessage);\n onSuccess?.(result);\n } catch (err: any) {\n const errorMessage = err.message || mergedCopy.errorMessage;\n setError(errorMessage);\n onError?.(errorMessage);\n } finally {\n setLoading(false);\n }\n };\n\n const getInputStyle = (field: 'email' | 'name') => ({\n ...mergedStyles.input,\n ...(fieldErrors[field] ? mergedStyles.inputError : {}),\n });\n\n const getButtonStyle = () => ({\n ...mergedStyles.button,\n ...(loading || verifying ? mergedStyles.buttonLoading : {}),\n ...(!email || loading || verifying ? mergedStyles.buttonDisabled : {}),\n });\n\n if (verifying) {\n return (\n <div className={className} style={mergedStyles.container}>\n <h2 style={mergedStyles.title}>{mergedCopy.verifyingText}</h2>\n <div style={mergedStyles.verifyingContainer}>\n <div style={mergedStyles.verifyingText}>{mergedCopy.verifyingDescription}</div>\n </div>\n </div>\n );\n }\n\n return (\n <div className={className} style={mergedStyles.container}>\n <h2 style={mergedStyles.title}>{mergedCopy.title}</h2>\n <p style={mergedStyles.description}>{mergedCopy.description}</p>\n\n <form onSubmit={handleSubmit} style={mergedStyles.form}>\n <div style={mergedStyles.fieldGroup}>\n <label style={mergedStyles.label}>{mergedCopy.emailLabel}</label>\n <input\n id=\"email\"\n name=\"email\"\n type=\"email\"\n value={email}\n onChange={e => {\n setEmail(e.target.value);\n if (fieldErrors.email) {\n setFieldErrors(prev => ({ ...prev, email: false }));\n }\n }}\n placeholder={mergedCopy.emailPlaceholder}\n style={getInputStyle('email')}\n disabled={loading || verifying}\n />\n </div>\n\n {/* Toggle to show name fields for new users */}\n {!showNameFields && (\n <div style={mergedStyles.toggleContainer}>\n <button\n type=\"button\"\n onClick={() => setShowNameFields(true)}\n style={mergedStyles.toggleLink}\n >\n {mergedCopy.showNameToggle}\n </button>\n </div>\n )}\n\n {showNameFields && (\n <>\n <div style={mergedStyles.fieldGroup}>\n <label style={mergedStyles.label}>{mergedCopy.nameLabel}</label>\n <input\n id=\"name\"\n name=\"name\"\n type=\"text\"\n value={name}\n onChange={e => {\n setName(e.target.value);\n if (fieldErrors.name) {\n setFieldErrors(prev => ({ ...prev, name: false }));\n }\n }}\n placeholder={mergedCopy.namePlaceholder}\n style={getInputStyle('name')}\n disabled={loading || verifying}\n />\n </div>\n\n <div style={mergedStyles.fieldGroup}>\n <label style={mergedStyles.label}>{mergedCopy.lastNameLabel}</label>\n <input\n id=\"lastName\"\n name=\"lastName\"\n type=\"text\"\n value={lastName}\n onChange={e => setLastName(e.target.value)}\n placeholder={mergedCopy.lastNamePlaceholder}\n style={mergedStyles.input}\n disabled={loading || verifying}\n />\n </div>\n\n <div style={mergedStyles.toggleContainer}>\n <button\n type=\"button\"\n onClick={() => {\n setShowNameFields(false);\n setName('');\n setLastName('');\n }}\n style={mergedStyles.toggleLink}\n >\n {mergedCopy.hideNameToggle}\n </button>\n </div>\n </>\n )}\n\n <button type=\"submit\" disabled={!email || loading || verifying} style={getButtonStyle()}>\n {loading ? mergedCopy.loadingText : mergedCopy.submitButton}\n </button>\n\n {error && <div style={mergedStyles.errorText}>{error}</div>}\n {success && <div style={mergedStyles.successText}>{success}</div>}\n </form>\n\n {showTraditionalLinks && (\n <div style={mergedStyles.linkContainer}>\n <div>\n <span style={mergedStyles.divider}>{mergedCopy.loginText} </span>\n <a onClick={onLoginClick} style={mergedStyles.link}>\n {mergedCopy.loginLink}\n </a>\n </div>\n\n <div style={mergedStyles.divider}>{mergedCopy.dividerBullet}</div>\n\n <div>\n <span style={mergedStyles.divider}>{mergedCopy.signupText} </span>\n <a onClick={onSignupClick} style={mergedStyles.link}>\n {mergedCopy.signupLink}\n </a>\n </div>\n </div>\n )}\n </div>\n );\n}\n","import React, { useState, useEffect } from 'react';\nimport { useAuth } from '../providers/AuthProvider';\n\nexport interface MagicLinkVerifyCopy {\n title?: string;\n verifyingMessage?: string;\n successMessage?: string;\n errorMessage?: string;\n redirectingMessage?: string;\n retryButton?: string;\n backToLoginButton?: string;\n missingParamsError?: string;\n}\n\nexport interface MagicLinkVerifyStyles {\n container?: React.CSSProperties;\n card?: React.CSSProperties;\n title?: React.CSSProperties;\n message?: React.CSSProperties;\n successMessage?: React.CSSProperties;\n errorMessage?: React.CSSProperties;\n spinner?: React.CSSProperties;\n buttonContainer?: React.CSSProperties;\n retryButton?: React.CSSProperties;\n retryButtonHover?: React.CSSProperties;\n backButton?: React.CSSProperties;\n backButtonHover?: React.CSSProperties;\n}\n\nexport interface MagicLinkVerifyIcons {\n loading?: React.ReactNode;\n success?: React.ReactNode;\n error?: React.ReactNode;\n}\n\nexport interface MagicLinkVerifyProps {\n copy?: MagicLinkVerifyCopy;\n styles?: MagicLinkVerifyStyles;\n icons?: MagicLinkVerifyIcons;\n onSuccess?: (data: any) => void;\n onError?: (error: string) => void;\n onRetry?: () => void;\n onBackToLogin?: () => void;\n className?: string;\n // Auto-extract from URL params if not provided\n token?: string;\n email?: string;\n appId?: string;\n tenantSlug?: string;\n // Auto-redirect after success (in milliseconds)\n autoRedirectDelay?: number;\n}\n\nconst defaultCopy: Required<MagicLinkVerifyCopy> = {\n title: 'Verifying Magic Link',\n verifyingMessage: 'Please wait while we verify your magic link...',\n successMessage: 'Magic link verified successfully! You are now logged in.',\n errorMessage: 'Failed to verify magic link. The link may be expired or invalid.',\n redirectingMessage: 'Redirecting you to the dashboard...',\n retryButton: 'Try Again',\n backToLoginButton: 'Back to Login',\n missingParamsError: 'Missing required parameters: token or email',\n};\n\nconst defaultStyles: Required<MagicLinkVerifyStyles> = {\n container: {\n maxWidth: '400px',\n width: '100%',\n margin: '0 auto',\n padding: '2rem',\n backgroundColor: '#ffffff',\n borderRadius: '8px',\n boxShadow: '0 2px 10px rgba(0, 0, 0, 0.1)',\n },\n card: {\n // Not used in new design, keeping for compatibility\n backgroundColor: 'transparent',\n padding: '0',\n borderRadius: '0',\n boxShadow: 'none',\n maxWidth: '100%',\n width: '100%',\n textAlign: 'center',\n },\n title: {\n fontSize: '1.5rem',\n fontWeight: 'bold',\n textAlign: 'center',\n marginBottom: '1.5rem',\n color: '#333333',\n },\n message: {\n fontSize: '1rem',\n color: '#6b7280',\n marginBottom: '1.5rem',\n lineHeight: '1.5',\n textAlign: 'center',\n },\n successMessage: {\n fontSize: '1rem',\n color: '#059669',\n marginBottom: '1.5rem',\n lineHeight: '1.5',\n textAlign: 'center',\n },\n errorMessage: {\n fontSize: '0.875rem',\n color: '#ef4444',\n textAlign: 'center',\n marginBottom: '1rem',\n lineHeight: '1.5',\n },\n spinner: {\n display: 'inline-block',\n width: '20px',\n height: '20px',\n border: '2px solid #e5e7eb',\n borderTop: '2px solid #3b82f6',\n borderRadius: '50%',\n animation: 'spin 1s linear infinite',\n marginRight: '0.5rem',\n },\n buttonContainer: {\n display: 'flex',\n gap: '0.75rem',\n justifyContent: 'center',\n flexWrap: 'wrap',\n marginTop: '1rem',\n },\n retryButton: {\n padding: '0.75rem 1rem',\n backgroundColor: '#3b82f6',\n color: 'white',\n border: 'none',\n borderRadius: '6px',\n fontSize: '1rem',\n fontWeight: '500',\n cursor: 'pointer',\n transition: 'background-color 0.15s ease-in-out',\n },\n backButton: {\n padding: '0.75rem 1rem',\n backgroundColor: '#f3f4f6',\n color: '#374151',\n border: '1px solid #d1d5db',\n borderRadius: '6px',\n fontSize: '1rem',\n fontWeight: '500',\n cursor: 'pointer',\n transition: 'all 0.15s ease-in-out',\n },\n retryButtonHover: {\n backgroundColor: '#2563eb',\n },\n backButtonHover: {\n backgroundColor: '#e5e7eb',\n },\n};\n\n// Loading spinner icon\nconst LoadingIcon = () => <div style={defaultStyles.spinner} />;\n\n// Success icon\nconst SuccessIcon = () => (\n <svg\n width=\"48\"\n height=\"48\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"#059669\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n style={{ margin: '0 auto 1rem auto', display: 'block' }}\n >\n <path d=\"M22 11.08V12a10 10 0 1 1-5.93-9.14\" />\n <polyline points=\"22,4 12,14.01 9,11.01\" />\n </svg>\n);\n\n// Error icon\nconst ErrorIcon = () => (\n <svg\n width=\"48\"\n height=\"48\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"#ef4444\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n style={{ margin: '0 auto 1rem auto', display: 'block' }}\n >\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <line x1=\"15\" y1=\"9\" x2=\"9\" y2=\"15\" />\n <line x1=\"9\" y1=\"9\" x2=\"15\" y2=\"15\" />\n </svg>\n);\n\nconst defaultIcons: Required<MagicLinkVerifyIcons> = {\n loading: <LoadingIcon />,\n success: <SuccessIcon />,\n error: <ErrorIcon />,\n};\n\ntype VerificationState = 'verifying' | 'success' | 'error' | 'redirecting';\n\nexport function MagicLinkVerify({\n copy = {},\n styles = {},\n icons = {},\n onSuccess,\n onError,\n onRetry,\n onBackToLogin,\n className,\n token: propToken,\n email: propEmail,\n appId: propAppId,\n tenantSlug: propTenantSlug,\n autoRedirectDelay = 3000,\n}: MagicLinkVerifyProps) {\n const [state, setState] = useState<VerificationState>('verifying');\n const [error, setError] = useState('');\n\n const { verifyMagicLink } = useAuth();\n\n const mergedCopy = { ...defaultCopy, ...copy };\n const mergedStyles = { ...defaultStyles, ...styles };\n const mergedIcons = { ...defaultIcons, ...icons };\n\n // Extract parameters from URL or use props\n const getUrlParams = () => {\n if (typeof window === 'undefined') return {};\n\n const urlParams = new URLSearchParams(window.location.search);\n return {\n token: propToken || urlParams.get('token') || '',\n email: propEmail || urlParams.get('email') || '',\n appId: propAppId || urlParams.get('appId') || '',\n tenantSlug: propTenantSlug || urlParams.get('tenantSlug') || undefined,\n };\n };\n\n const handleVerification = async () => {\n setState('verifying');\n setError('');\n\n try {\n const params = getUrlParams();\n\n if (!params.token || !params.email) {\n throw new Error(mergedCopy.missingParamsError);\n }\n\n const result = await verifyMagicLink({\n token: params.token,\n email: params.email,\n tenantSlug: params.tenantSlug,\n });\n\n setState('success');\n onSuccess?.(result);\n\n // Auto-redirect after success\n if (autoRedirectDelay > 0) {\n setTimeout(() => {\n setState('redirecting');\n }, autoRedirectDelay);\n }\n } catch (err: any) {\n const errorMessage = err.message || mergedCopy.errorMessage;\n setError(errorMessage);\n setState('error');\n onError?.(errorMessage);\n }\n };\n\n const handleRetry = () => {\n onRetry?.();\n handleVerification();\n };\n\n const handleBackToLogin = () => {\n onBackToLogin?.();\n };\n\n // Auto-verify on mount\n useEffect(() => {\n handleVerification();\n }, []);\n\n const renderContent = () => {\n switch (state) {\n case 'verifying':\n return (\n <div style={mergedStyles.message}>\n {mergedIcons.loading}\n {mergedCopy.verifyingMessage}\n </div>\n );\n\n case 'success':\n return (\n <>\n {mergedIcons.success}\n <div style={mergedStyles.successMessage}>{mergedCopy.successMessage}</div>\n </>\n );\n\n case 'redirecting':\n return (\n <>\n {mergedIcons.loading}\n <div style={mergedStyles.message}>{mergedCopy.redirectingMessage}</div>\n </>\n );\n\n case 'error':\n return (\n <>\n {mergedIcons.error}\n <div style={mergedStyles.errorMessage}>{error || mergedCopy.errorMessage}</div>\n <div style={mergedStyles.buttonContainer}>\n <button\n onClick={handleRetry}\n style={mergedStyles.retryButton}\n onMouseOver={e => {\n Object.assign(e.currentTarget.style, mergedStyles.retryButtonHover);\n }}\n onMouseOut={e => {\n const base = mergedStyles.retryButton || {};\n Object.keys(mergedStyles.retryButtonHover || {}).forEach(key => {\n (e.currentTarget.style as any)[key] = (base as any)[key] ?? '';\n });\n }}\n >\n {mergedCopy.retryButton}\n </button>\n <button\n onClick={handleBackToLogin}\n style={mergedStyles.backButton}\n onMouseOver={e => {\n Object.assign(e.currentTarget.style, mergedStyles.backButtonHover);\n }}\n onMouseOut={e => {\n const base = mergedStyles.backButton || {};\n Object.keys(mergedStyles.backButtonHover || {}).forEach(key => {\n (e.currentTarget.style as any)[key] = (base as any)[key] ?? '';\n });\n }}\n >\n {mergedCopy.backToLoginButton}\n </button>\n </div>\n </>\n );\n\n default:\n return null;\n }\n };\n\n return (\n <div style={mergedStyles.container} className={className}>\n <style>\n {`\n @keyframes spin {\n 0% { transform: rotate(0deg); }\n 100% { transform: rotate(360deg); }\n }\n `}\n </style>\n <h1 style={mergedStyles.title}>{mergedCopy.title}</h1>\n {renderContent()}\n </div>\n );\n}\n","import React, { useState } from 'react';\nimport { useAuth } from '../providers/AuthProvider';\nimport { useTenantInfo } from '../providers/TenantProvider';\n\nexport interface PasswordRecoveryFormCopy {\n title?: string;\n subtitle?: string;\n emailLabel?: string;\n emailPlaceholder?: string;\n submitButton?: string;\n backToLoginLink?: string;\n successMessage?: string;\n errorMessage?: string;\n loadingText?: string;\n // Reset form copy\n resetTitle?: string;\n resetSubtitle?: string;\n tokenLabel?: string;\n tokenPlaceholder?: string;\n newPasswordLabel?: string;\n newPasswordPlaceholder?: string;\n confirmPasswordLabel?: string;\n confirmPasswordPlaceholder?: string;\n resetSubmitButton?: string;\n resetLoadingText?: string;\n resetSuccessMessage?: string;\n passwordMismatchError?: string;\n requestNewLinkLink?: string;\n haveTokenLink?: string;\n tenantNotFoundError?: string;\n dividerBullet?: string;\n}\n\nexport interface PasswordRecoveryFormStyles {\n container?: React.CSSProperties;\n title?: React.CSSProperties;\n subtitle?: React.CSSProperties;\n form?: React.CSSProperties;\n fieldGroup?: React.CSSProperties;\n label?: React.CSSProperties;\n input?: React.CSSProperties;\n inputError?: React.CSSProperties;\n button?: React.CSSProperties;\n buttonDisabled?: React.CSSProperties;\n buttonLoading?: React.CSSProperties;\n errorText?: React.CSSProperties;\n successText?: React.CSSProperties;\n linkContainer?: React.CSSProperties;\n link?: React.CSSProperties;\n modeSwitchDivider?: React.CSSProperties;\n}\n\nexport interface PasswordRecoveryFormProps {\n copy?: PasswordRecoveryFormCopy;\n styles?: PasswordRecoveryFormStyles;\n mode?: 'request' | 'reset';\n token?: string;\n onSuccess?: (data?: any) => void;\n onError?: (error: string) => void;\n onBackToLogin?: () => void;\n onModeChange?: (mode: 'request' | 'reset') => void;\n className?: string;\n}\n\nconst defaultCopy: Required<PasswordRecoveryFormCopy> = {\n title: 'Reset Password',\n subtitle: \"Enter your email address and we'll send you a link to reset your password.\",\n emailLabel: 'Email',\n emailPlaceholder: 'Enter your email',\n submitButton: 'Send Reset Link',\n backToLoginLink: 'Back to Sign In',\n successMessage: 'Password reset link sent! Check your email.',\n errorMessage: 'Failed to send reset link',\n loadingText: 'Sending...',\n resetTitle: 'Set New Password',\n resetSubtitle: 'Enter your reset token and new password.',\n tokenLabel: 'Reset Token',\n tokenPlaceholder: 'Enter reset token from email',\n newPasswordLabel: 'New Password',\n newPasswordPlaceholder: 'Enter new password',\n confirmPasswordLabel: 'Confirm Password',\n confirmPasswordPlaceholder: 'Confirm new password',\n resetSubmitButton: 'Reset Password',\n resetLoadingText: 'Resetting...',\n resetSuccessMessage: 'Password reset successfully!',\n passwordMismatchError: 'Passwords do not match',\n requestNewLinkLink: 'Request New Link',\n haveTokenLink: 'I have a token',\n tenantNotFoundError: 'Tenant not found',\n dividerBullet: '•',\n};\n\nconst defaultStyles: Required<PasswordRecoveryFormStyles> = {\n container: {\n maxWidth: '400px',\n margin: '0 auto',\n padding: '2rem',\n backgroundColor: '#ffffff',\n borderRadius: '8px',\n boxShadow: '0 2px 10px rgba(0, 0, 0, 0.1)',\n },\n title: {\n fontSize: '1.5rem',\n fontWeight: 'bold',\n textAlign: 'center',\n marginBottom: '0.5rem',\n color: '#333333',\n },\n subtitle: {\n fontSize: '0.875rem',\n textAlign: 'center',\n marginBottom: '1.5rem',\n color: '#6b7280',\n lineHeight: '1.4',\n },\n form: {\n display: 'flex',\n flexDirection: 'column',\n gap: '1rem',\n },\n fieldGroup: {\n display: 'flex',\n flexDirection: 'column',\n gap: '0.5rem',\n },\n label: {\n fontSize: '0.875rem',\n fontWeight: '500',\n color: '#374151',\n },\n input: {\n padding: '0.75rem',\n border: '1px solid #d1d5db',\n borderRadius: '6px',\n fontSize: '1rem',\n transition: 'border-color 0.15s ease-in-out',\n outline: 'none',\n },\n inputError: {\n borderColor: '#ef4444',\n boxShadow: '0 0 0 3px rgba(239, 68, 68, 0.1)',\n },\n button: {\n padding: '0.75rem 1rem',\n backgroundColor: '#f59e0b',\n color: 'white',\n border: 'none',\n borderRadius: '6px',\n fontSize: '1rem',\n fontWeight: '500',\n cursor: 'pointer',\n transition: 'background-color 0.15s ease-in-out',\n marginTop: '0.5rem',\n },\n buttonDisabled: {\n backgroundColor: '#9ca3af',\n cursor: 'not-allowed',\n },\n buttonLoading: {\n backgroundColor: '#6b7280',\n },\n errorText: {\n color: '#ef4444',\n fontSize: '0.875rem',\n textAlign: 'center',\n marginTop: '0.5rem',\n },\n successText: {\n color: '#10b981',\n fontSize: '0.875rem',\n textAlign: 'center',\n marginTop: '0.5rem',\n },\n linkContainer: {\n textAlign: 'center',\n marginTop: '1rem',\n },\n link: {\n color: '#3b82f6',\n textDecoration: 'none',\n fontSize: '0.875rem',\n cursor: 'pointer',\n },\n modeSwitchDivider: {\n margin: '0 0.5rem',\n color: '#6b7280',\n },\n};\n\nexport function PasswordRecoveryForm({\n copy = {},\n styles = {},\n mode = 'request',\n token: initialToken = '',\n onSuccess,\n onError,\n onBackToLogin,\n onModeChange,\n className,\n}: PasswordRecoveryFormProps) {\n const [email, setEmail] = useState('');\n const [token, setToken] = useState(initialToken);\n const [newPassword, setNewPassword] = useState('');\n const [confirmPassword, setConfirmPassword] = useState('');\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState('');\n const [success, setSuccess] = useState('');\n const [fieldErrors, setFieldErrors] = useState<{\n email?: boolean;\n token?: boolean;\n newPassword?: boolean;\n confirmPassword?: boolean;\n }>({});\n\n const { requestPasswordReset, confirmPasswordReset } = useAuth();\n const { tenant } = useTenantInfo();\n\n const mergedCopy = { ...defaultCopy, ...copy };\n const mergedStyles = { ...defaultStyles, ...styles };\n\n const validateRequestForm = () => {\n const errors: { email?: boolean } = {};\n if (!email.trim()) errors.email = true;\n setFieldErrors(errors);\n return Object.keys(errors).length === 0;\n };\n\n const validateResetForm = () => {\n const errors: { token?: boolean; newPassword?: boolean; confirmPassword?: boolean } = {};\n if (!token.trim()) errors.token = true;\n if (!newPassword.trim()) errors.newPassword = true;\n if (!confirmPassword.trim()) errors.confirmPassword = true;\n setFieldErrors(errors);\n return Object.keys(errors).length === 0;\n };\n\n const handleRequestSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n\n if (!validateRequestForm()) return;\n if (!tenant?.id) {\n setError(mergedCopy.tenantNotFoundError);\n return;\n }\n\n setLoading(true);\n setError('');\n setSuccess('');\n\n try {\n await requestPasswordReset({ email, tenantId: tenant.id });\n setSuccess(mergedCopy.successMessage);\n onSuccess?.();\n } catch (err: any) {\n const errorMessage = err.message || mergedCopy.errorMessage;\n setError(errorMessage);\n onError?.(errorMessage);\n } finally {\n setLoading(false);\n }\n };\n\n const handleResetSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n\n if (!validateResetForm()) return;\n\n if (newPassword !== confirmPassword) {\n setError(mergedCopy.passwordMismatchError);\n setFieldErrors({ confirmPassword: true });\n return;\n }\n\n setLoading(true);\n setError('');\n setSuccess('');\n\n try {\n await confirmPasswordReset({ token, newPassword });\n setSuccess(mergedCopy.resetSuccessMessage);\n onSuccess?.();\n } catch (err: any) {\n const errorMessage = err.message || mergedCopy.errorMessage;\n setError(errorMessage);\n onError?.(errorMessage);\n } finally {\n setLoading(false);\n }\n };\n\n const getInputStyle = (field: keyof typeof fieldErrors) => ({\n ...mergedStyles.input,\n ...(fieldErrors[field] ? mergedStyles.inputError : {}),\n });\n\n const getButtonStyle = () => ({\n ...mergedStyles.button,\n ...(loading ? mergedStyles.buttonLoading : {}),\n });\n\n if (mode === 'reset') {\n const isFormValid = token && newPassword && confirmPassword;\n\n return (\n <div className={className} style={mergedStyles.container}>\n <h2 style={mergedStyles.title}>{mergedCopy.resetTitle}</h2>\n <p style={mergedStyles.subtitle}>{mergedCopy.resetSubtitle}</p>\n\n <form onSubmit={handleResetSubmit} style={mergedStyles.form}>\n <div style={mergedStyles.fieldGroup}>\n <label style={mergedStyles.label}>{mergedCopy.tokenLabel}</label>\n <input\n type=\"text\"\n value={token}\n onChange={e => {\n setToken(e.target.value);\n if (fieldErrors.token) {\n setFieldErrors(prev => ({ ...prev, token: false }));\n }\n }}\n placeholder={mergedCopy.tokenPlaceholder}\n style={getInputStyle('token')}\n disabled={loading}\n />\n </div>\n\n <div style={mergedStyles.fieldGroup}>\n <label style={mergedStyles.label}>{mergedCopy.newPasswordLabel}</label>\n <input\n type=\"password\"\n value={newPassword}\n onChange={e => {\n setNewPassword(e.target.value);\n if (fieldErrors.newPassword) {\n setFieldErrors(prev => ({ ...prev, newPassword: false }));\n }\n }}\n placeholder={mergedCopy.newPasswordPlaceholder}\n style={getInputStyle('newPassword')}\n disabled={loading}\n />\n </div>\n\n <div style={mergedStyles.fieldGroup}>\n <label style={mergedStyles.label}>{mergedCopy.confirmPasswordLabel}</label>\n <input\n type=\"password\"\n value={confirmPassword}\n onChange={e => {\n setConfirmPassword(e.target.value);\n if (fieldErrors.confirmPassword) {\n setFieldErrors(prev => ({ ...prev, confirmPassword: false }));\n }\n if (error === mergedCopy.passwordMismatchError) {\n setError('');\n }\n }}\n placeholder={mergedCopy.confirmPasswordPlaceholder}\n style={getInputStyle('confirmPassword')}\n disabled={loading}\n />\n </div>\n\n <button\n type=\"submit\"\n disabled={!isFormValid || loading}\n style={{\n ...getButtonStyle(),\n ...(!isFormValid || loading ? mergedStyles.buttonDisabled : {}),\n }}\n >\n {loading ? mergedCopy.resetLoadingText : mergedCopy.resetSubmitButton}\n </button>\n\n {error && <div style={mergedStyles.errorText}>{error}</div>}\n {success && <div style={mergedStyles.successText}>{success}</div>}\n </form>\n\n <div style={mergedStyles.linkContainer}>\n <a onClick={onBackToLogin} style={mergedStyles.link}>\n {mergedCopy.backToLoginLink}\n </a>\n {onModeChange && (\n <>\n <span style={mergedStyles.modeSwitchDivider}>{mergedCopy.dividerBullet}</span>\n <a onClick={() => onModeChange('request')} style={mergedStyles.link}>\n {mergedCopy.requestNewLinkLink}\n </a>\n </>\n )}\n </div>\n </div>\n );\n }\n\n // Request mode\n const isFormValid = email;\n\n return (\n <div className={className} style={mergedStyles.container}>\n <h2 style={mergedStyles.title}>{mergedCopy.title}</h2>\n <p style={mergedStyles.subtitle}>{mergedCopy.subtitle}</p>\n\n <form onSubmit={handleRequestSubmit} style={mergedStyles.form}>\n <div style={mergedStyles.fieldGroup}>\n <label style={mergedStyles.label}>{mergedCopy.emailLabel}</label>\n <input\n type=\"email\"\n value={email}\n onChange={e => {\n setEmail(e.target.value);\n if (fieldErrors.email) {\n setFieldErrors(prev => ({ ...prev, email: false }));\n }\n }}\n placeholder={mergedCopy.emailPlaceholder}\n style={getInputStyle('email')}\n disabled={loading}\n />\n </div>\n\n <button\n type=\"submit\"\n disabled={!isFormValid || loading}\n style={{\n ...getButtonStyle(),\n ...(!isFormValid || loading ? mergedStyles.buttonDisabled : {}),\n }}\n >\n {loading ? mergedCopy.loadingText : mergedCopy.submitButton}\n </button>\n\n {error && <div style={mergedStyles.errorText}>{error}</div>}\n {success && <div style={mergedStyles.successText}>{success}</div>}\n </form>\n\n <div style={mergedStyles.linkContainer}>\n <a onClick={onBackToLogin} style={mergedStyles.link}>\n {mergedCopy.backToLoginLink}\n </a>\n {onModeChange && (\n <>\n <span style={mergedStyles.modeSwitchDivider}>{mergedCopy.dividerBullet}</span>\n <a onClick={() => onModeChange('reset')} style={mergedStyles.link}>\n {mergedCopy.haveTokenLink}\n </a>\n </>\n )}\n </div>\n </div>\n );\n}\n","import { ReactNode } from 'react';\nimport { useApp } from '../providers/AppProvider';\nimport { useTenantOptional } from '../providers/TenantProvider';\nimport { useAuthOptional } from '../providers/AuthProvider';\nimport { useFeatureFlagsOptional } from '../providers/FeatureFlagProvider';\nimport { useSubscriptionOptional } from '../providers/SubscriptionProvider';\n\nexport interface AppLoaderProps {\n children: ReactNode;\n loadingFallback?: ReactNode;\n errorFallback?: ReactNode | ((error: Error, retry: () => void) => ReactNode);\n // Optional: require tenant to be loaded (default: true)\n requireTenant?: boolean;\n}\n\n// Default loading component\nconst DefaultLoadingFallback = () => (\n <div\n style={{\n display: 'flex',\n justifyContent: 'center',\n alignItems: 'center',\n height: '100vh',\n fontFamily: 'system-ui, sans-serif',\n }}\n >\n <div>Loading...</div>\n </div>\n);\n\n// Default error component\nconst DefaultErrorFallback = ({ error, retry }: { error: Error; retry: () => void }) => (\n <div\n style={{\n display: 'flex',\n flexDirection: 'column',\n justifyContent: 'center',\n alignItems: 'center',\n height: '100vh',\n fontFamily: 'system-ui, sans-serif',\n textAlign: 'center',\n padding: '20px',\n }}\n >\n <h2 style={{ color: '#dc3545', marginBottom: '16px' }}>Error</h2>\n <p style={{ color: '#6c757d', marginBottom: '24px' }}>\n {error.message || 'Unable to load application'}\n </p>\n <button\n onClick={retry}\n style={{\n padding: '8px 16px',\n backgroundColor: '#007bff',\n color: 'white',\n border: 'none',\n borderRadius: '4px',\n cursor: 'pointer',\n }}\n >\n Retry\n </button>\n </div>\n);\n\n/**\n * AppLoader - Blocks rendering until App (and optionally Tenant) data are loaded\n *\n * Must be used inside AppProvider. TenantProvider is optional.\n *\n * @example\n * ```tsx\n * // With TenantProvider (parallel loading)\n * <AppProvider config={appConfig}>\n * <TenantProvider config={tenantConfig}>\n * <AppLoader loadingFallback={<Spinner />}>\n * <App />\n * </AppLoader>\n * </TenantProvider>\n * </AppProvider>\n *\n * // Without TenantProvider (app only)\n * <AppProvider config={appConfig}>\n * <AppLoader loadingFallback={<Spinner />}>\n * <App />\n * </AppLoader>\n * </AppProvider>\n * ```\n */\nexport function AppLoader({\n children,\n loadingFallback,\n errorFallback,\n requireTenant = true,\n}: AppLoaderProps) {\n // AppProvider is required - useApp will throw if not present\n const { isAppLoading, appError, retryApp } = useApp();\n\n // TenantProvider is optional\n const tenantContext = useTenantOptional();\n\n // Optional providers\n const authContext = useAuthOptional();\n const featureFlagsContext = useFeatureFlagsOptional();\n const subscriptionContext = useSubscriptionOptional();\n\n // Extract tenant state (if TenantProvider is present)\n const isTenantLoading = tenantContext?.isTenantLoading ?? false;\n const tenantError = tenantContext?.tenantError ?? null;\n const tenantSlug = tenantContext?.tenantSlug ?? null;\n const retryTenant = tenantContext?.retryTenant ?? (() => {});\n\n // Extract ready states from optional providers (default to ready if not present)\n const isAuthReady = authContext?.isAuthReady ?? true;\n const isFeatureFlagsReady = featureFlagsContext?.isReady ?? true;\n const isSubscriptionReady = subscriptionContext?.isReady ?? true;\n\n // Determine if we're still loading\n // Only wait for tenant if: requireTenant is true AND TenantProvider exists AND there's a tenantSlug\n const shouldWaitForTenant = requireTenant && tenantContext && tenantSlug;\n // Wait for optional providers if they exist and are not ready\n const shouldWaitForAuth = authContext && !isAuthReady;\n const shouldWaitForFeatureFlags = featureFlagsContext && !isFeatureFlagsReady;\n const shouldWaitForSubscription = subscriptionContext && !isSubscriptionReady;\n\n const isLoading =\n isAppLoading ||\n (shouldWaitForTenant && isTenantLoading) ||\n shouldWaitForAuth ||\n shouldWaitForFeatureFlags ||\n shouldWaitForSubscription;\n\n // Combine errors - app error takes priority\n const error = appError || (shouldWaitForTenant ? tenantError : null);\n\n // Combined retry function\n const retry = () => {\n if (appError) {\n retryApp();\n }\n if (tenantError && tenantContext) {\n retryTenant();\n }\n };\n\n // Show loading state\n if (isLoading) {\n return <>{loadingFallback || <DefaultLoadingFallback />}</>;\n }\n\n // Show error state\n if (error) {\n const ErrorComponent =\n typeof errorFallback === 'function'\n ? errorFallback(error, retry)\n : errorFallback || <DefaultErrorFallback error={error} retry={retry} />;\n\n return <>{ErrorComponent}</>;\n }\n\n // Both app and tenant are ready\n return <>{children}</>;\n}\n\n/**\n * Hook to get the combined loading state of App and Tenant\n * Useful for showing loading indicators without blocking rendering\n *\n * Must be used inside AppProvider. TenantProvider is optional.\n */\nexport function useAppLoaderState(requireTenant = true) {\n // AppProvider is required - useApp will throw if not present\n const { isAppLoading, appError, retryApp, appInfo } = useApp();\n\n // Optional providers\n const tenantContext = useTenantOptional();\n const authContext = useAuthOptional();\n const featureFlagsContext = useFeatureFlagsOptional();\n const subscriptionContext = useSubscriptionOptional();\n\n const isTenantLoading = tenantContext?.isTenantLoading ?? false;\n const tenantError = tenantContext?.tenantError ?? null;\n const tenant = tenantContext?.tenant ?? null;\n const tenantSlug = tenantContext?.tenantSlug ?? null;\n const retryTenant = tenantContext?.retryTenant ?? (() => {});\n\n const isAuthReady = authContext?.isAuthReady ?? true;\n const isFeatureFlagsReady = featureFlagsContext?.isReady ?? true;\n const isSubscriptionReady = subscriptionContext?.isReady ?? true;\n\n const shouldWaitForTenant = requireTenant && tenantContext && tenantSlug;\n const shouldWaitForAuth = authContext && !isAuthReady;\n const shouldWaitForFeatureFlags = featureFlagsContext && !isFeatureFlagsReady;\n const shouldWaitForSubscription = subscriptionContext && !isSubscriptionReady;\n\n const isLoading =\n isAppLoading ||\n (shouldWaitForTenant && isTenantLoading) ||\n shouldWaitForAuth ||\n shouldWaitForFeatureFlags ||\n shouldWaitForSubscription;\n\n const error = appError || (shouldWaitForTenant ? tenantError : null);\n const isReady =\n !isLoading && !error && appInfo !== null && (!shouldWaitForTenant || tenant !== null);\n\n const retry = () => {\n if (appError) retryApp();\n if (tenantError && tenantContext) retryTenant();\n };\n\n return {\n isLoading,\n error,\n isReady,\n retry,\n // Individual states\n app: { isLoading: isAppLoading, error: appError, data: appInfo },\n tenant: tenantContext ? { isLoading: isTenantLoading, error: tenantError, data: tenant } : null,\n auth: authContext ? { isReady: isAuthReady } : null,\n featureFlags: featureFlagsContext ? { isReady: isFeatureFlagsReady } : null,\n subscription: subscriptionContext ? { isReady: isSubscriptionReady } : null,\n };\n}\n","import React, { useState, useRef, useEffect } from 'react';\nimport { useAuthOptional } from '../providers/AuthProvider';\nimport type { UserTenantMembership } from '../types/api';\n\nexport interface TenantSelectorStyles {\n wrapper?: React.CSSProperties;\n button?: React.CSSProperties;\n buttonDisabled?: React.CSSProperties;\n dropdown?: React.CSSProperties;\n item?: React.CSSProperties;\n itemSelected?: React.CSSProperties;\n itemHover?: React.CSSProperties;\n itemRole?: React.CSSProperties;\n arrow?: React.CSSProperties;\n}\n\nconst defaultStyles: Required<TenantSelectorStyles> = {\n wrapper: {\n position: 'relative',\n },\n button: {\n cursor: 'pointer',\n opacity: 1,\n },\n buttonDisabled: {\n cursor: 'not-allowed',\n opacity: 0.6,\n },\n dropdown: {\n position: 'absolute',\n top: '100%',\n left: 0,\n right: 0,\n zIndex: 1000,\n backgroundColor: 'white',\n border: '1px solid #ccc',\n borderRadius: 4,\n boxShadow: '0 2px 8px rgba(0,0,0,0.15)',\n maxHeight: 300,\n overflowY: 'auto',\n },\n item: {\n padding: '8px 12px',\n cursor: 'pointer',\n backgroundColor: 'transparent',\n },\n itemSelected: {\n backgroundColor: '#f0f0f0',\n },\n itemHover: {\n backgroundColor: '#f5f5f5',\n },\n itemRole: {\n opacity: 0.7,\n marginLeft: 8,\n },\n arrow: {\n marginLeft: 8,\n },\n};\n\nexport interface TenantSelectorProps {\n tenants?: UserTenantMembership[];\n currentTenantId?: string | null;\n onSelect?: (tenantId: string) => void;\n styles?: TenantSelectorStyles;\n className?: string;\n dropdownClassName?: string;\n itemClassName?: string;\n renderItem?: (tenant: UserTenantMembership, isSelected: boolean) => React.ReactNode;\n placeholder?: string;\n disabled?: boolean;\n showCurrentTenant?: boolean;\n}\n\nexport function TenantSelector({\n tenants: propTenants,\n currentTenantId: propCurrentTenantId,\n onSelect: propOnSelect,\n styles: propStyles = {},\n className = '',\n dropdownClassName = '',\n itemClassName = '',\n renderItem,\n placeholder = 'Select tenant',\n disabled = false,\n showCurrentTenant = true,\n}: TenantSelectorProps) {\n const mergedStyles = { ...defaultStyles, ...propStyles };\n const auth = useAuthOptional();\n const [isOpen, setIsOpen] = useState(false);\n const dropdownRef = useRef<HTMLDivElement>(null);\n\n // Use props if provided, otherwise fall back to context\n const tenants = propTenants ?? auth?.userTenants ?? [];\n const currentTenantId = propCurrentTenantId ?? auth?.currentUser?.tenantId ?? null;\n\n const handleSelect = async (tenantId: string) => {\n setIsOpen(false);\n if (propOnSelect) {\n propOnSelect(tenantId);\n } else if (auth?.switchToTenant) {\n await auth.switchToTenant(tenantId);\n }\n };\n\n // Close dropdown when clicking outside\n useEffect(() => {\n const handleClickOutside = (event: MouseEvent) => {\n if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {\n setIsOpen(false);\n }\n };\n\n document.addEventListener('mousedown', handleClickOutside);\n return () => document.removeEventListener('mousedown', handleClickOutside);\n }, []);\n\n // Find current tenant\n const currentTenant = tenants.find(t => t.id === currentTenantId);\n\n // Don't render if no tenants available\n if (tenants.length === 0) {\n return null;\n }\n\n // Don't render selector if only one tenant and showing current\n if (tenants.length === 1 && showCurrentTenant) {\n return (\n <div className={className}>\n <span>{tenants[0].name}</span>\n </div>\n );\n }\n\n const defaultRenderItem = (tenant: UserTenantMembership, isSelected: boolean) => (\n <span style={{ fontWeight: isSelected ? 'bold' : 'normal' }}>\n {tenant.name}\n {tenant.role && <span style={mergedStyles.itemRole}>({tenant.role})</span>}\n </span>\n );\n\n return (\n <div ref={dropdownRef} className={className} style={mergedStyles.wrapper}>\n <button\n type=\"button\"\n onClick={() => !disabled && setIsOpen(!isOpen)}\n disabled={disabled}\n style={{\n ...mergedStyles.button,\n ...(disabled ? mergedStyles.buttonDisabled : {}),\n }}\n >\n {currentTenant ? currentTenant.name : placeholder}\n <span style={mergedStyles.arrow}>{isOpen ? '▲' : '▼'}</span>\n </button>\n\n {isOpen && (\n <div className={dropdownClassName} style={mergedStyles.dropdown}>\n {tenants.map(tenant => {\n const isSelected = tenant.id === currentTenantId;\n return (\n <div\n key={tenant.id}\n className={itemClassName}\n onClick={() => handleSelect(tenant.id)}\n style={{\n ...mergedStyles.item,\n ...(isSelected ? mergedStyles.itemSelected : {}),\n }}\n onMouseEnter={e => {\n if (!isSelected) {\n Object.assign(e.currentTarget.style, mergedStyles.itemHover);\n }\n }}\n onMouseLeave={e => {\n if (!isSelected) {\n const base = mergedStyles.item || {};\n Object.keys(mergedStyles.itemHover || {}).forEach(key => {\n (e.currentTarget.style as any)[key] = (base as any)[key] ?? '';\n });\n }\n }}\n >\n {renderItem\n ? renderItem(tenant, isSelected)\n : defaultRenderItem(tenant, isSelected)}\n </div>\n );\n })}\n </div>\n )}\n </div>\n );\n}\n\nexport default TenantSelector;\n","import { HttpService } from './HttpService';\nimport { SessionManager } from './SessionManager';\nimport type {\n Permission,\n CreatePermissionRequest,\n ApiResponse,\n PaginationParams,\n} from '../types/api';\n\nexport class PermissionApiService {\n constructor(\n private httpService: HttpService,\n private sessionManager?: SessionManager\n ) {}\n\n async createPermission(request: CreatePermissionRequest): Promise<Permission> {\n if (!this.sessionManager) {\n throw new Error('SessionManager is required for private endpoints');\n }\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const response = await this.httpService.post<ApiResponse<Permission>>(\n '/permissions/',\n request,\n {\n headers: authHeaders,\n }\n );\n return response.data;\n }\n\n async getPermissions(\n params?: PaginationParams\n ): Promise<{ permissions: Permission[]; meta: any }> {\n if (!this.sessionManager) {\n throw new Error('SessionManager is required for private endpoints');\n }\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const queryParams = new URLSearchParams();\n\n if (params?.page) queryParams.append('page', params.page.toString());\n if (params?.limit) queryParams.append('limit', params.limit.toString());\n if (params?.sortBy) queryParams.append('sortBy', params.sortBy);\n if (params?.sortOrder) queryParams.append('sortOrder', params.sortOrder);\n\n const url = `/permissions/${queryParams.toString() ? `?${queryParams.toString()}` : ''}`;\n const response = await this.httpService.get<ApiResponse<Permission[]>>(url, {\n headers: authHeaders,\n });\n\n return {\n permissions: response.data,\n meta: response.meta,\n };\n }\n\n async getPermissionById(id: string): Promise<Permission> {\n if (!this.sessionManager) {\n throw new Error('SessionManager is required for private endpoints');\n }\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const response = await this.httpService.get<ApiResponse<Permission>>(`/permissions/${id}`, {\n headers: authHeaders,\n });\n return response.data;\n }\n\n async updatePermission(\n id: string,\n request: Partial<CreatePermissionRequest>\n ): Promise<Permission> {\n if (!this.sessionManager) {\n throw new Error('SessionManager is required for private endpoints');\n }\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const response = await this.httpService.put<ApiResponse<Permission>>(\n `/permissions/${id}`,\n request,\n {\n headers: authHeaders,\n }\n );\n return response.data;\n }\n\n async deletePermission(id: string): Promise<void> {\n if (!this.sessionManager) {\n throw new Error('SessionManager is required for private endpoints');\n }\n const authHeaders = await this.sessionManager.getAuthHeaders();\n await this.httpService.delete<void>(`/permissions/${id}`, {\n headers: authHeaders,\n });\n }\n\n // Public endpoint - no auth required\n async getAppPermissions(\n appId: string,\n params?: PaginationParams\n ): Promise<{ permissions: Permission[]; meta: any }> {\n const queryParams = new URLSearchParams();\n\n if (params?.page) queryParams.append('page', params.page.toString());\n if (params?.limit) queryParams.append('limit', params.limit.toString());\n if (params?.sortBy) queryParams.append('sortBy', params.sortBy);\n if (params?.sortOrder) queryParams.append('sortOrder', params.sortOrder);\n\n const url = `/permissions/apps/${appId}${queryParams.toString() ? `?${queryParams.toString()}` : ''}`;\n const response = await this.httpService.get<ApiResponse<Permission[]>>(url);\n\n return {\n permissions: response.data,\n meta: response.meta,\n };\n }\n}\n","import { HttpService } from './HttpService';\nimport { SessionManager } from './SessionManager';\nimport type {\n SubscriptionPlan,\n CreateSubscriptionPlanRequest,\n ApiResponse,\n PaginationParams,\n} from '../types/api';\n\nexport class SubscriptionPlanApiService {\n constructor(\n private httpService: HttpService,\n private sessionManager: SessionManager\n ) {}\n\n async createSubscriptionPlan(request: CreateSubscriptionPlanRequest): Promise<SubscriptionPlan> {\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const response = await this.httpService.post<ApiResponse<SubscriptionPlan>>(\n '/subscription-plans/',\n request,\n {\n headers: authHeaders,\n }\n );\n return response.data;\n }\n\n async getSubscriptionPlans(\n params?: PaginationParams & { appId?: string }\n ): Promise<{ plans: SubscriptionPlan[]; meta: any }> {\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const queryParams = new URLSearchParams();\n\n if (params?.page) queryParams.append('page', params.page.toString());\n if (params?.limit) queryParams.append('limit', params.limit.toString());\n if (params?.sortBy) queryParams.append('sortBy', params.sortBy);\n if (params?.sortOrder) queryParams.append('sortOrder', params.sortOrder);\n if (params?.appId) queryParams.append('appId', params.appId);\n\n const url = `/subscription-plans/${queryParams.toString() ? `?${queryParams.toString()}` : ''}`;\n const response = await this.httpService.get<ApiResponse<SubscriptionPlan[]>>(url, {\n headers: authHeaders,\n });\n\n return {\n plans: response.data,\n meta: response.meta,\n };\n }\n\n async getSubscriptionPlanById(id: string): Promise<SubscriptionPlan> {\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const response = await this.httpService.get<ApiResponse<SubscriptionPlan>>(\n `/subscription-plans/${id}`,\n {\n headers: authHeaders,\n }\n );\n return response.data;\n }\n\n async updateSubscriptionPlan(\n id: string,\n request: Partial<CreateSubscriptionPlanRequest>\n ): Promise<SubscriptionPlan> {\n const authHeaders = await this.sessionManager.getAuthHeaders();\n const response = await this.httpService.put<ApiResponse<SubscriptionPlan>>(\n `/subscription-plans/${id}`,\n request,\n {\n headers: authHeaders,\n }\n );\n return response.data;\n }\n\n async deleteSubscriptionPlan(id: string): Promise<void> {\n const authHeaders = await this.sessionManager.getAuthHeaders();\n await this.httpService.delete<void>(`/subscription-plans/${id}`, {\n headers: authHeaders,\n });\n }\n}\n","import { HttpService } from './HttpService';\n\nexport class HealthApiService {\n constructor(private httpService: HttpService) {}\n\n // Public endpoint - no auth required\n async checkHealth(): Promise<{ status: string }> {\n return await this.httpService.get<{ status: string }>('/health');\n }\n}\n","// Data transformation utilities for API responses\n\nexport class ApiMappers {\n // Date string to Date object\n static toDate(dateString: string): Date {\n return new Date(dateString);\n }\n\n // Date object to ISO string\n static toISOString(date: Date): string {\n return date.toISOString();\n }\n\n // Transform API response pagination meta\n static transformPaginationMeta(meta: any) {\n return {\n total: meta.total || 0,\n page: meta.page || 1,\n limit: meta.limit || 100,\n totalPages: meta.totalPages || 1,\n hasNext: meta.hasNext || false,\n hasPrev: meta.hasPrev || false,\n };\n }\n\n // Transform user data for display\n static transformUser(user: any) {\n return {\n ...user,\n createdAt: this.toDate(user.createdAt),\n updatedAt: this.toDate(user.updatedAt),\n displayName: user.lastName ? `${user.name} ${user.lastName}` : user.name,\n isActiveUser: user.isActive,\n };\n }\n\n // Transform role data for display\n static transformRole(role: any) {\n return {\n ...role,\n createdAt: this.toDate(role.createdAt),\n updatedAt: this.toDate(role.updatedAt),\n permissionCount: role.permissions?.length || 0,\n };\n }\n\n // Transform tenant data for display\n static transformTenant(tenant: any) {\n return {\n ...tenant,\n createdAt: this.toDate(tenant.createdAt),\n updatedAt: this.toDate(tenant.updatedAt),\n displayName: tenant.name,\n hasCustomDomain: !!tenant.domain,\n };\n }\n\n // Transform subscription data for display\n static transformSubscription(subscription: any) {\n return {\n ...subscription,\n createdAt: this.toDate(subscription.createdAt),\n updatedAt: this.toDate(subscription.updatedAt),\n startDate: this.toDate(subscription.startDate),\n endDate: subscription.endDate ? this.toDate(subscription.endDate) : null,\n isActive: subscription.status === 'ACTIVE',\n isExpired: subscription.endDate ? new Date(subscription.endDate) < new Date() : false,\n };\n }\n\n // Transform app data for display\n static transformApp(app: any) {\n return {\n ...app,\n createdAt: this.toDate(app.createdAt),\n updatedAt: this.toDate(app.updatedAt),\n isAdminLevel: app.securityLevel === 'ADMIN',\n hasDefaultPlan: !!app.defaultSubscriptionPlanId,\n };\n }\n\n // Transform feature flag data for display\n static transformFeatureFlag(featureFlag: any) {\n return {\n ...featureFlag,\n createdAt: this.toDate(featureFlag.createdAt),\n updatedAt: this.toDate(featureFlag.updatedAt),\n isEnabled: featureFlag.isActive,\n };\n }\n\n // Transform permission data for display\n static transformPermission(permission: any) {\n return {\n ...permission,\n createdAt: this.toDate(permission.createdAt),\n updatedAt: this.toDate(permission.updatedAt),\n fullName: `${permission.resource}:${permission.action}`,\n isSystemLevel: !permission.appId,\n };\n }\n\n // Transform subscription plan data for display\n static transformSubscriptionPlan(plan: any) {\n return {\n ...plan,\n createdAt: this.toDate(plan.createdAt),\n updatedAt: this.toDate(plan.updatedAt),\n displayPrice: `${plan.currency} ${plan.price}`,\n isMonthly: plan.billingCycle === 'MONTHLY',\n featureCount: plan.features?.length || 0,\n };\n }\n\n // Transform error response\n static transformError(error: any) {\n return {\n code: error.error?.code || 'UNKNOWN_ERROR',\n message: error.message || 'An unexpected error occurred',\n type: error.type || 'SYSTEM',\n isAuthError: error.type === 'AUTH',\n isValidationError: error.type === 'VALIDATION',\n };\n }\n\n // Transform query parameters for API calls\n static transformQueryParams(params: any): URLSearchParams {\n const searchParams = new URLSearchParams();\n\n Object.entries(params).forEach(([key, value]) => {\n if (value !== undefined && value !== null && value !== '') {\n searchParams.append(key, String(value));\n }\n });\n\n return searchParams;\n }\n}\n","import { useCallback, useMemo } from 'react';\nimport { useNavigate, useSearchParams } from 'react-router-dom';\nimport { useAuth } from '../providers/AuthProvider';\nimport { useTenant } from '../providers/TenantProvider';\nimport { UserType } from '../types/api';\nimport {\n DEFAULT_ZONE_ROOTS,\n ReturnToStorage,\n ZoneRoots,\n UseZoneNavigationReturn,\n} from '../types/zoneRouting';\n\nconst DEFAULT_RETURN_TO_PARAM = 'returnTo';\nconst RETURN_TO_SESSION_KEY = 'zone_return_to';\nconst RETURN_TO_LOCAL_KEY = 'zone_return_to';\n\ninterface UseZoneNavigationOptions {\n zoneRoots?: ZoneRoots;\n returnToParam?: string;\n returnToStorage?: ReturnToStorage;\n}\n\n/**\n * Hook for zone-based navigation utilities\n */\nexport function useZoneNavigation(options: UseZoneNavigationOptions = {}): UseZoneNavigationReturn {\n const {\n zoneRoots = {},\n returnToParam = DEFAULT_RETURN_TO_PARAM,\n returnToStorage = 'url',\n } = options;\n\n const navigate = useNavigate();\n const [searchParams, setSearchParams] = useSearchParams();\n const { isAuthenticated, currentUser } = useAuth();\n const { tenant } = useTenant();\n\n const mergedZoneRoots = useMemo(() => ({ ...DEFAULT_ZONE_ROOTS, ...zoneRoots }), [zoneRoots]);\n\n const hasTenant = Boolean(tenant);\n const userType = currentUser?.userType;\n\n /**\n * Get return URL from storage\n */\n const returnToUrl = useMemo((): string | null => {\n switch (returnToStorage) {\n case 'url':\n return searchParams.get(returnToParam);\n case 'session':\n return sessionStorage.getItem(RETURN_TO_SESSION_KEY);\n case 'local':\n return localStorage.getItem(RETURN_TO_LOCAL_KEY);\n default:\n return null;\n }\n }, [returnToStorage, searchParams, returnToParam]);\n\n /**\n * Clear return URL from storage\n */\n const clearReturnTo = useCallback(() => {\n switch (returnToStorage) {\n case 'url': {\n const newParams = new URLSearchParams(searchParams);\n newParams.delete(returnToParam);\n setSearchParams(newParams, { replace: true });\n break;\n }\n case 'session':\n sessionStorage.removeItem(RETURN_TO_SESSION_KEY);\n break;\n case 'local':\n localStorage.removeItem(RETURN_TO_LOCAL_KEY);\n break;\n }\n }, [returnToStorage, searchParams, returnToParam, setSearchParams]);\n\n /**\n * Set return URL in storage\n */\n const setReturnTo = useCallback(\n (url: string) => {\n switch (returnToStorage) {\n case 'url': {\n const newParams = new URLSearchParams(searchParams);\n newParams.set(returnToParam, url);\n setSearchParams(newParams, { replace: true });\n break;\n }\n case 'session':\n sessionStorage.setItem(RETURN_TO_SESSION_KEY, url);\n break;\n case 'local':\n localStorage.setItem(RETURN_TO_LOCAL_KEY, url);\n break;\n }\n },\n [returnToStorage, searchParams, returnToParam, setSearchParams]\n );\n\n /**\n * Navigate to a specific zone root\n */\n const navigateToZone = useCallback(\n (zone: keyof ZoneRoots) => {\n const path = mergedZoneRoots[zone] || mergedZoneRoots.default;\n navigate(path);\n },\n [navigate, mergedZoneRoots]\n );\n\n /**\n * Get smart redirect based on current state\n */\n const getSmartRedirect = useCallback((): string => {\n if (!hasTenant) {\n // No tenant context\n if (!isAuthenticated) {\n return mergedZoneRoots.publicGuest;\n }\n if (userType === UserType.TENANT_ADMIN) {\n return mergedZoneRoots.publicAdmin;\n }\n return mergedZoneRoots.publicUser;\n } else {\n // Has tenant context\n if (!isAuthenticated) {\n return mergedZoneRoots.tenantGuest;\n }\n if (userType === UserType.TENANT_ADMIN) {\n return mergedZoneRoots.tenantAdmin;\n }\n return mergedZoneRoots.tenantUser;\n }\n }, [hasTenant, isAuthenticated, userType, mergedZoneRoots]);\n\n return {\n returnToUrl,\n clearReturnTo,\n setReturnTo,\n navigateToZone,\n getSmartRedirect,\n };\n}\n\n/**\n * Build redirect URL with return path\n */\nexport function buildRedirectUrl(\n redirectTo: string,\n returnPath: string | null,\n returnToParam: string = DEFAULT_RETURN_TO_PARAM,\n returnToStorage: ReturnToStorage = 'url'\n): string {\n if (!returnPath || returnToStorage !== 'url') {\n return redirectTo;\n }\n\n const url = new URL(redirectTo, window.location.origin);\n url.searchParams.set(returnToParam, returnPath);\n return url.pathname + url.search;\n}\n"],"names":["HttpService","baseUrl","timeout","sessionManager","method","endpoint","data","options","url","requestTimeout","requestHeaders","accessToken","controller","timeoutId","response","contentType","error","AppApiService","httpService","request","authHeaders","params","queryParams","id","appId","planId","schema","defaultSettings","AppContext","createContext","AppProvider","config","children","cacheConfig","useMemo","_a","_b","_c","appInfo","setAppInfo","useState","cached","parsed","isAppLoading","setIsAppLoading","appError","setAppError","contextValue","retryApp","loadApp","useCallback","bypassCache","appData","cacheData","err","backgroundRefresh","useEffect","useApp","context","useContext","useAppOptional","useApi","SessionExpiredError","reason","message","defaultMessages","TokenRefreshTimeoutError","timeoutMs","TokenRefreshError","attempts","lastError","_SessionManager","key","existing","instance","storageKey","stored","parts","payload","tokens","expiresAt","tokenData","currentData","refreshToken","expiresIn","tokenType","resolvedExpiresAt","token","delay","gen","resolve","reject","idx","e","newTokens","newAccessToken","queue","entry","attempt","backoff","networkError","errorMessage","body","refreshResponse","user","expiredError","decodedPayload","ms","SessionManager","AuthApiService","RoleApiService","roleId","userId","UserApiService","TenantApiService","slug","detectSubdomainTenant","hostname","baseDomain","baseDomainLower","currentHost","subdomain","detectSelectorTenant","search","selectorParam","localStorage","urlTenant","detectTenantSlug","location","tenantMode","fixedTenantSlug","buildTenantHostname","targetTenantSlug","currentHostname","AUTH_TRANSFER_PARAM","encodeAuthTokens","decodeAuthTokens","encoded","base64","extractAuthTokensFromUrl","clearAuthTokensFromUrl","TenantContext","TenantProvider","detectTenant","tenantSlug","setTenantSlug","tenant","setTenant","isTenantLoading","setIsTenantLoading","tenantError","setTenantError","settings","setSettings","isSettingsLoading","setIsSettingsLoading","settingsError","setSettingsError","detected","settingsSchema","loadTenant","tenantInfo","loadSettings","tenantSettings","refreshSettings","validateSettings","settingsToValidate","errors","fieldSchema","value","expectedType","actualType","switchTenant","mode","redirectPath","newHostname","targetPath","urlParams","newUrl","useTenant","useTenantOptional","useTenantSettings","useSettings","useTenantInfo","retryTenant","AuthContext","AuthProvider","availableRoles","setAvailableRoles","rolesLoading","setRolesLoading","currentUser","setCurrentUser","isUserLoading","setIsUserLoading","userError","setUserError","userTenants","setUserTenants","hasTenantContext","setHasTenantContext","initRef","useRef","isLoadingAfterUrlTokens","setIsLoadingAfterUrlTokens","manager","isRestoringSession","setIsRestoringSession","isAuthReady","authenticatedHttpService","service","authApiService","userApiService","roleApiService","userRole","role","userPermissions","isAuthenticated","loadUserDataRef","loadUserData","forceRefresh","userData","refreshUser","login","username","password","targetSlug","resolvedTenantId","targetSessionManager","loginResponse","shouldSwitch","hasTenant","autoSwitch","singleTenant","signup","email","phoneNumber","name","lastName","tenantId","signupTenantAdmin","tenantName","changePassword","currentPassword","newPassword","requestPasswordReset","confirmPasswordReset","sendMagicLink","frontendUrl","verifyMagicLink","verifyResponse","logout","setTokens","hasValidSession","clearSession","fetchRoles","roles","refreshRoles","hasPermission","permission","permissionString","permissions","targetTenant","t","tenants","internalHttpService","urlTokensCleanedUp","setUrlTokensCleanedUp","cancelled","useAuth","useAuthOptional","FeatureFlagApiService","flagKey","FeatureFlagContext","FeatureFlagProvider","appContext","tenantContext","featureFlags","setFeatureFlags","loading","setLoading","setError","initialLoadDone","setInitialLoadDone","featureFlagService","fetchFeatureFlags","refreshInterval","interval","isEnabled","flag","f","getFlag","getFlagState","refresh","isReady","useFeatureFlags","useFeatureFlagsOptional","SubscriptionApiService","subscriptionId","paymentData","SubscriptionContext","SubscriptionProvider","subscription","setSubscription","subscriptionService","fetchSubscription","features","isFeatureEnabled","featureKey","feature","getFeature","getFeatureValue","defaultValue","hasAllowedPlan","allowedPlans","useSubscription","useSubscriptionOptional","UserType","DEFAULT_ZONE_ROOTS","DEFAULT_ZONE_PRESETS","RoutingContext","RoutingProvider","zoneRoots","presets","useRouting","useRoutingOptional","DefaultFallback","jsxs","jsx","InsufficientPermissionsFallback","userType","minUserType","missingPermissions","Fragment","hasMinimumUserType","hierarchy","Protected","fallback","requiredPermissions","requireAllPermissions","hasAnyPermission","hasAllPermissions","requiredUserType","hasRequiredUserType","ProtectedRoute","redirectTo","useLocation","Navigate","DefaultTenantRequiredFallback","TenantRoute","isLoading","DefaultTenantDetectedFallback","LandingRoute","matchesUserType","currentType","required","checkAccessMode","condition","getAccessDeniedType","requirements","state","perms","p","getSmartRedirect","buildRedirectUrl","returnPath","currentPath","returnToParam","returnToStorage","returnUrl","separator","storeReturnUrl","ZoneRoute","preset","authMode","returnTo","onAccessDenied","loadingFallback","accessDeniedFallback","isAuthInitializing","routingConfig","presetConfig","accessDeniedType","redirectTarget","accessDeniedReason","finalRedirect","TenantZone","props","PublicZone","AuthenticatedZone","GuestZone","AdminZone","UserZone","OpenZone","TenantAuthenticatedZone","TenantOpenZone","TenantGuestZone","SubscriptionGuard","requiredFeature","flagName","FeatureFlag","EyeIcon","EyeOffIcon","defaultIcons","defaultCopy","defaultStyles","LoginForm","copy","styles","icons","onSuccess","onError","onForgotPassword","onSignupClick","onMagicLinkClick","showForgotPassword","showSignupLink","showMagicLinkOption","className","setUsername","setPassword","showPassword","setShowPassword","fieldErrors","setFieldErrors","mergedCopy","mergedStyles","mergedIcons","validateForm","handleSubmit","result","getInputStyle","field","getButtonStyle","prev","SignupForm","signupType","onLoginClick","showLoginLink","setName","setLastName","setEmail","setPhoneNumber","confirmPassword","setConfirmPassword","setTenantName","isFormValid","MagicLinkForm","showTraditionalLinks","verifyToken","verifying","setVerifying","success","setSuccess","showNameFields","setShowNameFields","handleVerifyMagicLink","finalFrontendUrl","LoadingIcon","SuccessIcon","ErrorIcon","MagicLinkVerify","onRetry","onBackToLogin","propToken","propEmail","propAppId","propTenantSlug","autoRedirectDelay","setState","getUrlParams","handleVerification","handleRetry","handleBackToLogin","renderContent","base","PasswordRecoveryForm","initialToken","onModeChange","setToken","setNewPassword","validateRequestForm","validateResetForm","handleRequestSubmit","handleResetSubmit","DefaultLoadingFallback","DefaultErrorFallback","retry","AppLoader","errorFallback","requireTenant","authContext","featureFlagsContext","subscriptionContext","isFeatureFlagsReady","isSubscriptionReady","shouldWaitForTenant","ErrorComponent","useAppLoaderState","TenantSelector","propTenants","propCurrentTenantId","propOnSelect","propStyles","dropdownClassName","itemClassName","renderItem","placeholder","disabled","showCurrentTenant","auth","isOpen","setIsOpen","dropdownRef","currentTenantId","handleSelect","handleClickOutside","event","currentTenant","defaultRenderItem","isSelected","PermissionApiService","SubscriptionPlanApiService","HealthApiService","ApiMappers","dateString","date","meta","app","featureFlag","plan","searchParams","DEFAULT_RETURN_TO_PARAM","RETURN_TO_SESSION_KEY","RETURN_TO_LOCAL_KEY","useZoneNavigation","navigate","useNavigate","setSearchParams","useSearchParams","mergedZoneRoots","returnToUrl","clearReturnTo","newParams","setReturnTo","navigateToZone","zone","path"],"mappings":"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"}
|