@skylabs-digital/react-identity-access 3.0.0 → 3.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +439 -352
- package/dist/errors/SessionErrors.d.ts +10 -0
- package/dist/errors/SessionErrors.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 +1083 -934
- package/dist/index.es.js.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/services/AuthApiService.d.ts +1 -0
- package/dist/services/AuthApiService.d.ts.map +1 -1
- package/dist/services/SessionManager.d.ts +10 -3
- package/dist/services/SessionManager.d.ts.map +1 -1
- package/dist/utils/configValidation.d.ts +39 -0
- package/dist/utils/configValidation.d.ts.map +1 -0
- package/dist/utils/jwt.d.ts +25 -0
- package/dist/utils/jwt.d.ts.map +1 -0
- package/package.json +15 -16
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/services/HttpService.ts","../src/utils/query.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/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/hooks/useAuthForm.ts","../src/components/authFormShared.ts","../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/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","/**\n * Serialize params into a query-string suffix (including the leading '?').\n * Returns an empty string if no params are provided or none are set.\n */\nexport function buildPaginationQuery(params?: object): string {\n if (!params) return '';\n const qp = new URLSearchParams();\n for (const [key, value] of Object.entries(params)) {\n if (value !== undefined && value !== null && value !== '') {\n qp.append(key, String(value));\n }\n }\n const str = qp.toString();\n return str ? `?${str}` : '';\n}\n","import { HttpService } from './HttpService';\nimport type {\n ApiResponse,\n App,\n CreateAppRequest,\n PublicAppInfo,\n PaginationParams,\n} from '../types/api';\nimport { buildPaginationQuery } from '../utils/query';\n\nexport class AppApiService {\n constructor(private httpService: HttpService) {}\n\n async createApp(request: CreateAppRequest): Promise<App> {\n const response = await this.httpService.post<ApiResponse<App>>('/apps/', request);\n return response.data;\n }\n\n async getApps(params?: PaginationParams): Promise<{ apps: App[]; meta: any }> {\n const response = await this.httpService.get<ApiResponse<App[]>>(\n `/apps/${buildPaginationQuery(params)}`\n );\n return { apps: response.data, meta: response.meta };\n }\n\n async getAppById(id: string): Promise<App> {\n const response = await this.httpService.get<ApiResponse<App>>(`/apps/${id}`);\n return response.data;\n }\n\n async updateApp(id: string, request: Partial<CreateAppRequest>): Promise<App> {\n const response = await this.httpService.put<ApiResponse<App>>(`/apps/${id}`, request);\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 skipAuth: true,\n });\n return response.data;\n }\n\n async setDefaultSubscriptionPlan(appId: string, planId: string): Promise<App> {\n const response = await this.httpService.put<ApiResponse<App>>(\n `/apps/${appId}/default-subscription-plan`,\n { planId }\n );\n return response.data;\n }\n\n async updateSettingsSchema(appId: string, schema: any, defaultSettings: any): Promise<App> {\n const response = await this.httpService.put<ApiResponse<App>>(\n `/apps/${appId}/settings-schema`,\n { schema, defaultSettings }\n );\n return response.data;\n }\n\n async exportConfig(appId: string): Promise<any> {\n const response = await this.httpService.get<ApiResponse<any>>(`/apps/${appId}/export-config`);\n return response.data;\n }\n}\n","import {\n createContext,\n useContext,\n useMemo,\n ReactNode,\n useState,\n useEffect,\n useCallback,\n useRef,\n} from 'react';\nimport { HttpService } from '../services/HttpService';\nimport { AppApiService } from '../services/AppApiService';\nimport type { PublicAppInfo } from '../types/api';\n\ninterface CachedAppInfo {\n data: PublicAppInfo;\n timestamp: number;\n appId: string;\n}\n\nexport interface AppConfig {\n baseUrl: string;\n appId: string;\n cache?: {\n enabled?: boolean;\n ttl?: number;\n storageKey?: string;\n };\n}\n\ninterface AppContextValue {\n appId: string;\n baseUrl: string;\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\nconst DEFAULT_CACHE_TTL = 5 * 60 * 1000;\n\nexport function AppProvider({ config, children }: AppProviderProps) {\n const { appId, baseUrl } = config;\n const cacheEnabled = config.cache?.enabled ?? true;\n const cacheTtl = config.cache?.ttl ?? DEFAULT_CACHE_TTL;\n const cacheStorageKey = config.cache?.storageKey ?? `app_cache_${appId}`;\n\n const [appInfo, setAppInfo] = useState<PublicAppInfo | null>(() => {\n if (!cacheEnabled) return null;\n try {\n const cached = localStorage.getItem(cacheStorageKey);\n if (!cached) return null;\n const parsed: CachedAppInfo = JSON.parse(cached);\n if (Date.now() - parsed.timestamp < cacheTtl && parsed.appId === appId) {\n return parsed.data;\n }\n localStorage.removeItem(cacheStorageKey);\n return null;\n } catch {\n return null;\n }\n });\n\n const [isAppLoading, setIsAppLoading] = useState(!appInfo);\n const [appError, setAppError] = useState<Error | null>(null);\n\n const appInfoRef = useRef(appInfo);\n appInfoRef.current = appInfo;\n\n const loadApp = useCallback(\n async (bypassCache = false) => {\n if (!bypassCache && cacheEnabled && appInfoRef.current) return;\n\n try {\n setIsAppLoading(true);\n setAppError(null);\n\n const appApi = new AppApiService(new HttpService(baseUrl));\n const appData = await appApi.getPublicAppInfo(appId);\n setAppInfo(appData);\n\n if (cacheEnabled) {\n try {\n const cacheData: CachedAppInfo = {\n data: appData,\n timestamp: Date.now(),\n appId,\n };\n localStorage.setItem(cacheStorageKey, 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 [baseUrl, appId, cacheEnabled, cacheStorageKey]\n );\n\n const backgroundRefresh = useCallback(async () => {\n if (!cacheEnabled || !appInfoRef.current) return;\n\n try {\n const cached = localStorage.getItem(cacheStorageKey);\n if (!cached) return;\n\n const parsed: CachedAppInfo = JSON.parse(cached);\n if (Date.now() - parsed.timestamp <= cacheTtl * 0.5) return;\n\n const appApi = new AppApiService(new HttpService(baseUrl));\n const appData = await appApi.getPublicAppInfo(appId);\n setAppInfo(appData);\n\n const cacheData: CachedAppInfo = {\n data: appData,\n timestamp: Date.now(),\n appId,\n };\n localStorage.setItem(cacheStorageKey, JSON.stringify(cacheData));\n } catch (error) {\n if (process.env.NODE_ENV === 'development') {\n console.warn('[AppProvider] Background app refresh failed:', error);\n }\n }\n }, [baseUrl, appId, cacheEnabled, cacheTtl, cacheStorageKey]);\n\n const contextValue = useMemo<AppContextValue>(\n () => ({\n appId,\n baseUrl,\n appInfo,\n isAppLoading,\n appError,\n retryApp: () => {\n loadApp(true);\n },\n }),\n [appId, baseUrl, appInfo, isAppLoading, appError, loadApp]\n );\n\n useEffect(() => {\n if (appInfoRef.current) {\n backgroundRefresh();\n } else {\n loadApp();\n }\n // Run once on mount — loadApp/backgroundRefresh read the latest state via ref\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\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\nexport function useAppOptional(): AppContextValue | null {\n return useContext(AppContext);\n}\n\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 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 enableCookieSession?: boolean; // When true, sends credentials: 'include' on refresh calls for cross-subdomain cookie auth\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 return config.storageKey || '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 private enableCookieSession: boolean;\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 private consecutiveBackgroundFailures = 0;\n private static readonly MAX_BACKGROUND_FAILURES = 3;\n\n constructor(config: SessionConfig = {}) {\n this.storageKey = config.storageKey || 'auth_tokens';\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 this.enableCookieSession = config.enableCookieSession ?? false;\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 if (config.enableCookieSession !== undefined)\n this.enableCookieSession = config.enableCookieSession;\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 private static decodeJwtPayload(token: string): Record<string, unknown> | null {\n try {\n const parts = token.split('.');\n if (parts.length !== 3) return null;\n return JSON.parse(atob(parts[1].replace(/-/g, '+').replace(/_/g, '/')));\n } catch {\n return null;\n }\n }\n\n private static extractJwtExpiry(accessToken: string): number | undefined {\n const payload = SessionManager.decodeJwtPayload(accessToken);\n return typeof payload?.exp === 'number' ? payload.exp * 1000 : undefined;\n }\n\n private static extractJwtClaim(token: string, claim: string): string | undefined {\n const payload = SessionManager.decodeJwtPayload(token);\n const value = payload?.[claim];\n return typeof value === 'string' ? value : undefined;\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 — reset circuit breaker\n this.consecutiveBackgroundFailures = 0;\n })\n .catch(error => {\n if (error instanceof SessionExpiredError) {\n // Fatal — already handled by startRefreshAndResolveQueue\n } else if (this.sessionGeneration === gen) {\n // Circuit breaker: after MAX_BACKGROUND_FAILURES consecutive transient\n // failures, treat as fatal to prevent infinite retry loops\n this.consecutiveBackgroundFailures++;\n if (this.consecutiveBackgroundFailures >= SessionManager.MAX_BACKGROUND_FAILURES) {\n if (process.env.NODE_ENV === 'development') {\n console.error(\n `[SessionManager] Background refresh failed ${this.consecutiveBackgroundFailures} consecutive times — expiring session`\n );\n }\n this.consecutiveBackgroundFailures = 0;\n this.handleSessionExpired(\n new SessionExpiredError('token_invalid', 'Background refresh failed repeatedly')\n );\n return;\n }\n\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 /**\n * Attempt to restore a session using a backend-set HttpOnly cookie.\n * Sends a refresh request with credentials: 'include' but without a refresh token in the body.\n * If the backend responds with tokens (cookie carried the refresh token), stores them and returns true.\n * If it fails (no cookie, expired, etc.), returns false — this is a normal outcome, not an error.\n *\n * Only works when enableCookieSession is true.\n */\n async attemptCookieSessionRestore(): Promise<boolean> {\n if (!this.enableCookieSession || !this.baseUrl) return false;\n\n const url = `${this.baseUrl}/auth/refresh`;\n\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({}),\n credentials: 'include',\n });\n\n if (!response.ok) return false;\n\n const data = await response.json();\n if (!data.accessToken) return false;\n\n this.setTokens({\n accessToken: data.accessToken,\n refreshToken: data.refreshToken || '',\n expiresIn: data.expiresIn,\n });\n\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 // Use Web Locks API to coordinate refresh across browser tabs.\n // Without this, multiple tabs sharing localStorage can send the same\n // refresh token simultaneously, triggering \"reuse detected\" on servers\n // that rotate refresh tokens.\n if (typeof navigator !== 'undefined' && navigator.locks) {\n return navigator.locks.request(`session-refresh:${this.storageKey}`, () =>\n this.performTokenRefreshInner(refreshToken, gen)\n );\n }\n return this.performTokenRefreshInner(refreshToken, gen);\n }\n\n private async performTokenRefreshInner(refreshToken: string, gen: number): Promise<void> {\n if (!this.baseUrl) {\n throw new Error('Base URL not configured for token refresh');\n }\n\n // Re-read tokens from storage: another browser tab sharing localStorage\n // may have already refreshed while we were waiting for the lock or retrying.\n const freshTokens = this.getTokens();\n if (\n freshTokens?.accessToken &&\n !this.isTokenExpired(freshTokens) &&\n !this.shouldRefreshToken(freshTokens)\n ) {\n // Another tab already refreshed — the access token in storage is valid.\n // Skip the fetch entirely to avoid sending a stale refresh token\n // (which would trigger \"reuse detected\" on servers with token rotation).\n return;\n }\n\n // Use the freshest refresh token from storage. If another tab rotated it,\n // the old RT we received as parameter is now invalid — use the new one.\n const currentRefreshToken = freshTokens?.refreshToken || refreshToken;\n\n const url = `${this.baseUrl}/auth/refresh`;\n\n // Extract deviceId from the refresh token JWT if present.\n // Some backends require it as a separate body field for refresh.\n const deviceId = SessionManager.extractJwtClaim(currentRefreshToken, 'deviceId');\n const refreshBody: Record<string, string> = { refreshToken: currentRefreshToken };\n if (deviceId) {\n refreshBody.deviceId = deviceId;\n }\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(refreshBody),\n ...(this.enableCookieSession && { credentials: 'include' as RequestCredentials }),\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 // Token reuse / revocation — fatal (server revoked all sessions)\n if (errorMessage.includes('reuse') || errorMessage.includes('revoked')) {\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 || currentRefreshToken,\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 getTokenPayload(): JwtPayload | null {\n const token = this.getTokens()?.accessToken;\n if (!token) return null;\n return (SessionManager.decodeJwtPayload(token) as JwtPayload | null) ?? null;\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 // Prevents duplicate verifyMagicLink calls (React StrictMode double-mount)\n private pendingVerifications = new Map<string, Promise<VerifyMagicLinkResponse>>();\n\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(): Promise<UserTenantMembership[]> {\n return this.httpService.get<UserTenantMembership[]>('/auth/tenants');\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 key = request.token;\n const pending = this.pendingVerifications.get(key);\n if (pending) return pending;\n\n const promise = this.httpService\n .post<VerifyMagicLinkResponse>('/auth/magic-link/verify', request)\n .finally(() => {\n this.pendingVerifications.delete(key);\n });\n\n this.pendingVerifications.set(key, promise);\n return promise;\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 async changePassword(request: ChangePasswordRequest): Promise<void> {\n await this.httpService.post<ApiResponse<null>>('/auth/change-password', request);\n }\n}\n","import { HttpService } from './HttpService';\nimport type {\n Role,\n CreateRoleRequest,\n AssignRoleRequest,\n ApiResponse,\n PaginationParams,\n} from '../types/api';\nimport { buildPaginationQuery } from '../utils/query';\n\nexport class RoleApiService {\n constructor(private httpService: HttpService) {}\n\n async createRole(request: CreateRoleRequest): Promise<Role> {\n const response = await this.httpService.post<ApiResponse<Role>>('/roles/', request);\n return response.data;\n }\n\n async getRoleById(id: string): Promise<Role> {\n const response = await this.httpService.get<ApiResponse<Role>>(`/roles/${id}`);\n return response.data;\n }\n\n async updateRole(id: string, request: Partial<CreateRoleRequest>): Promise<Role> {\n const response = await this.httpService.put<ApiResponse<Role>>(`/roles/${id}`, request);\n return response.data;\n }\n\n async deleteRole(id: string): Promise<void> {\n await this.httpService.delete<void>(`/roles/${id}`);\n }\n\n async getRolesByApp(\n appId: string,\n params?: PaginationParams\n ): Promise<{ roles: Role[]; meta: any }> {\n const response = await this.httpService.get<ApiResponse<Role[]>>(\n `/roles/app/${appId}${buildPaginationQuery(params)}`,\n { skipAuth: true }\n );\n return { roles: response.data, meta: response.meta };\n }\n\n async assignRole(roleId: string, request: AssignRoleRequest): Promise<void> {\n await this.httpService.post<ApiResponse<null>>(`/roles/${roleId}/assign`, request);\n }\n\n async revokeRole(roleId: string, request: AssignRoleRequest): Promise<void> {\n await this.httpService.post<ApiResponse<null>>(`/roles/${roleId}/revoke`, request);\n }\n\n async getUserRoles(\n userId: string,\n params?: PaginationParams\n ): Promise<{ roles: Role[]; meta: any }> {\n const response = await this.httpService.get<ApiResponse<Role[]>>(\n `/roles/user/${userId}${buildPaginationQuery(params)}`\n );\n return { roles: response.data, meta: response.meta };\n }\n}\n","import { HttpService } from './HttpService';\nimport type { User, CreateUserRequest, ApiResponse, PaginationParams } from '../types/api';\nimport { buildPaginationQuery } from '../utils/query';\n\nexport class UserApiService {\n constructor(private httpService: HttpService) {}\n\n async createUser(request: CreateUserRequest): Promise<User> {\n const response = await this.httpService.post<ApiResponse<User>>('/users/', request);\n return response.data;\n }\n\n async getUsers(params?: PaginationParams): Promise<{ users: User[]; meta: any }> {\n const response = await this.httpService.get<ApiResponse<User[]>>(\n `/users/${buildPaginationQuery(params)}`\n );\n return { users: response.data, meta: response.meta };\n }\n\n async getUserById(id: string): Promise<User> {\n const response = await this.httpService.get<ApiResponse<User>>(`/users/${id}`);\n return response.data;\n }\n\n async updateUser(id: string, request: Partial<CreateUserRequest>): Promise<User> {\n const response = await this.httpService.put<ApiResponse<User>>(`/users/${id}`, request);\n return response.data;\n }\n\n async deleteUser(id: string): Promise<void> {\n await this.httpService.delete<void>(`/users/${id}`);\n }\n}\n","import { HttpService } from './HttpService';\nimport type {\n Tenant,\n CreateTenantRequest,\n PublicTenantInfo,\n TenantSettings,\n UpdateTenantSettingsRequest,\n ApiResponse,\n PaginationParams,\n} from '../types/api';\nimport { buildPaginationQuery } from '../utils/query';\n\nexport class TenantApiService {\n constructor(\n private httpService: HttpService,\n private appId?: string\n ) {}\n\n async createTenant(request: CreateTenantRequest): Promise<Tenant> {\n const response = await this.httpService.post<ApiResponse<Tenant>>('/tenants/', request);\n return response.data;\n }\n\n async getTenants(params?: PaginationParams): Promise<{ tenants: Tenant[]; meta: any }> {\n const response = await this.httpService.get<ApiResponse<Tenant[]>>(\n `/tenants/${buildPaginationQuery(params)}`\n );\n return { tenants: response.data, meta: response.meta };\n }\n\n async getTenantById(id: string): Promise<Tenant> {\n const response = await this.httpService.get<ApiResponse<Tenant>>(`/tenants/${id}`);\n return response.data;\n }\n\n async updateTenant(id: string, request: Partial<CreateTenantRequest>): Promise<Tenant> {\n const response = await this.httpService.put<ApiResponse<Tenant>>(`/tenants/${id}`, request);\n return response.data;\n }\n\n async adminUpdateTenant(id: string, request: Partial<CreateTenantRequest>): Promise<Tenant> {\n const response = await this.httpService.put<ApiResponse<Tenant>>(\n `/tenants/${id}/admin-update`,\n request\n );\n return response.data;\n }\n\n async getPublicTenantInfo(slug: string): Promise<PublicTenantInfo> {\n const response = await this.httpService.get<ApiResponse<PublicTenantInfo>>(\n `/tenants/${this.appId}/${slug}/public`,\n { skipAuth: true }\n );\n return response.data;\n }\n\n async getTenantSettings(id: string): Promise<TenantSettings> {\n const response = await this.httpService.get<ApiResponse<TenantSettings>>(\n `/tenants/${id}/settings`,\n { skipAuth: true }\n );\n return response.data;\n }\n\n async updateTenantSettings(\n id: string,\n request: UpdateTenantSettingsRequest\n ): Promise<TenantSettings> {\n const response = await this.httpService.put<ApiResponse<TenantSettings>>(\n `/tenants/${id}/settings`,\n request\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","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 type { TenantSettings, JSONSchema, PublicTenantInfo } from '../types/api';\n\n// 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 // 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'; 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 const cacheEnabled = config.cache?.enabled ?? true;\n const cacheTtl = config.cache?.ttl ?? 5 * 60 * 1000;\n const cacheStorageKey = config.cache?.storageKey ?? `tenant_cache_${tenantSlug || 'default'}`;\n const cacheConfig = useMemo(\n () => ({ enabled: cacheEnabled, ttl: cacheTtl, storageKey: cacheStorageKey }),\n [cacheEnabled, cacheTtl, cacheStorageKey]\n );\n\n // 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 // Load tenant info with caching\n const loadTenant = useCallback(\n async (slug: string, bypassCache = false) => {\n if (!bypassCache && cacheConfig.enabled && tenant && tenant.subdomain === slug) {\n return;\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 // 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 // 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 // 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 // Cross-subdomain auth is handled by enableCookieSession — the receiving\n // subdomain restores the session from a parent-domain HttpOnly refresh cookie.\n const switchTenant = useCallback(\n (\n targetTenantSlug: string,\n options?: { mode?: 'navigate' | 'reload'; redirectPath?: string }\n ) => {\n const { mode = 'reload', 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 if (redirectPath) {\n window.location.href = redirectPath;\n }\n return;\n }\n\n localStorage.setItem('tenant', targetTenantSlug);\n\n if (tenantMode === '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 const targetPath = redirectPath || window.location.pathname;\n const url = new URL(`${window.location.protocol}//${newHostname}${targetPath}`);\n\n const currentParams = new URLSearchParams(window.location.search);\n currentParams.forEach((value, key) => {\n url.searchParams.set(key, value);\n });\n\n window.location.href = url.toString();\n } else if (tenantMode === 'selector') {\n const targetPath = redirectPath || window.location.pathname;\n const urlParams = new URLSearchParams(window.location.search);\n urlParams.set(config.selectorParam || 'tenant', targetTenantSlug);\n\n if (mode === 'reload') {\n const newUrl = `${targetPath}?${urlParams.toString()}${window.location.hash}`;\n window.location.href = newUrl;\n } else {\n const newUrl = `${targetPath}?${urlParams.toString()}${window.location.hash}`;\n window.history.pushState({}, '', newUrl);\n setTenantSlug(targetTenantSlug);\n loadTenant(targetTenantSlug);\n }\n }\n },\n [config.tenantMode, config.selectorParam, config.baseDomain, config.fixedTenantSlug, 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 { useAppOptional } from './AppProvider';\nimport { useTenantOptional } from './TenantProvider';\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\nconst USER_TENANTS_STORAGE_KEY = 'userTenants';\n\nfunction readUserTenants(): UserTenantMembership[] {\n try {\n const cached = localStorage.getItem(USER_TENANTS_STORAGE_KEY);\n return cached ? JSON.parse(cached) : [];\n } catch {\n return [];\n }\n}\n\nfunction writeUserTenants(tenants: UserTenantMembership[]): void {\n try {\n localStorage.setItem(USER_TENANTS_STORAGE_KEY, JSON.stringify(tenants));\n } catch {\n // Ignore storage errors\n }\n}\n\nfunction clearUserTenants(): void {\n try {\n localStorage.removeItem(USER_TENANTS_STORAGE_KEY);\n } catch {\n // Ignore storage errors\n }\n}\n\nexport interface AuthConfig {\n /** @deprecated Use onSessionExpired instead */\n onRefreshFailed?: () => void;\n onSessionExpired?: (error: SessionExpiredError) => void;\n initialRoles?: Role[];\n refreshQueueTimeout?: number;\n proactiveRefreshMargin?: number;\n autoSwitchSingleTenant?: boolean;\n onTenantSelectionRequired?: (tenants: UserTenantMembership[]) => void;\n enableCookieSession?: boolean;\n baseUrl?: string;\n appId?: string;\n}\n\n/** Reactive auth state + permission helpers. Subscribe via useAuthState(). */\nexport interface AuthStateValue {\n isAuthenticated: boolean;\n isAuthInitializing: boolean;\n isAuthReady: boolean;\n currentUser: User | null;\n isUserLoading: boolean;\n userError: Error | null;\n userRole: Role | null;\n userPermissions: string[];\n availableRoles: Role[];\n rolesLoading: boolean;\n userTenants: UserTenantMembership[];\n hasTenantContext: boolean;\n sessionManager: SessionManager;\n authenticatedHttpService: HttpService;\n hasPermission: (permission: string | Permission) => boolean;\n hasAnyPermission: (permissions: (string | Permission)[]) => boolean;\n hasAllPermissions: (permissions: (string | Permission)[]) => boolean;\n getUserPermissionStrings: () => string[];\n}\n\n/** Stable auth action methods. Subscribe via useAuthActions() — never re-renders. */\nexport interface AuthActionsValue {\n login: (params: LoginParams) => Promise<LoginResponse>;\n signup: (params: SignupParams) => Promise<User>;\n signupTenantAdmin: (params: SignupTenantAdminParams) => Promise<{ user: User; tenant: any }>;\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 setTokens: (tokens: { accessToken: string; refreshToken: string; expiresIn: number }) => void;\n hasValidSession: () => boolean;\n clearSession: () => void;\n loadUserData: (forceRefresh?: boolean) => Promise<void>;\n refreshUser: () => Promise<void>;\n refreshRoles: () => Promise<void>;\n switchToTenant: (tenantId: string, options?: { redirectPath?: string }) => Promise<void>;\n refreshUserTenants: () => Promise<UserTenantMembership[]>;\n}\n\nexport type AuthContextValue = AuthStateValue & AuthActionsValue;\n\nconst AuthStateContext = createContext<AuthStateValue | null>(null);\nconst AuthActionsContext = createContext<AuthActionsValue | null>(null);\n\ninterface AuthProviderProps {\n config?: AuthConfig;\n children: ReactNode;\n}\n\nexport function AuthProvider({ config = {}, children }: AuthProviderProps) {\n const appContext = useAppOptional();\n const tenantContext = useTenantOptional();\n\n const baseUrl = appContext?.baseUrl ?? config.baseUrl ?? '';\n const appId = appContext?.appId ?? config.appId;\n const tenant = tenantContext?.tenant ?? null;\n const tenantSlug = tenantContext?.tenantSlug ?? null;\n const switchTenant = tenantContext?.switchTenant ?? (() => {});\n\n if (!baseUrl) {\n throw new Error(\n '[AuthProvider] baseUrl is required. Provide it via AppProvider or AuthConfig.baseUrl.'\n );\n }\n\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 const [userTenants, setUserTenants] = useState<UserTenantMembership[]>(() => readUserTenants());\n\n // === SYNCHRONOUS INITIALIZATION ===\n // Marks first-render init so downstream effects can gate on it.\n const initRef = useRef<{ done: boolean }>({ done: false });\n\n if (!initRef.current.done) {\n initRef.current.done = true;\n }\n\n const sessionManager = useMemo(() => {\n return SessionManager.getInstance({\n baseUrl,\n enableCookieSession: config.enableCookieSession,\n refreshQueueTimeout: config.refreshQueueTimeout,\n proactiveRefreshMargin: config.proactiveRefreshMargin,\n onSessionExpired: (error: SessionExpiredError) => {\n setCurrentUser(null);\n setUserError(null);\n setUserTenants([]);\n clearUserTenants();\n if (config.onSessionExpired) {\n config.onSessionExpired(error);\n } else if (config.onRefreshFailed) {\n config.onRefreshFailed();\n }\n },\n });\n }, [\n baseUrl,\n config.enableCookieSession,\n config.refreshQueueTimeout,\n config.proactiveRefreshMargin,\n ]);\n\n // CRITICAL: Initialize isRestoringSession 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 const tokens = sessionManager.getTokens();\n if (tokens) {\n return sessionManager.hasValidSession() || !!tokens.refreshToken;\n }\n if (config.enableCookieSession) return true;\n return false;\n });\n\n const isAuthReady = initRef.current.done && !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 () => new AuthApiService(authenticatedHttpService),\n [authenticatedHttpService]\n );\n\n const userApiService = useMemo(\n () => new UserApiService(authenticatedHttpService),\n [authenticatedHttpService]\n );\n\n const roleApiService = useMemo(\n () => new RoleApiService(authenticatedHttpService),\n [authenticatedHttpService]\n );\n\n const userRole = useMemo(() => {\n return currentUser?.roleId\n ? availableRoles.find(role => role.id === currentUser.roleId) || null\n : null;\n }, [currentUser, availableRoles]);\n\n const userPermissions = useMemo(() => userRole?.permissions || [], [userRole]);\n\n const isAuthenticated = useMemo(\n () => sessionManager.hasValidSession() && currentUser !== null,\n [sessionManager, currentUser]\n );\n\n const hasTenantContext = useMemo(() => currentUser?.tenantId != null, [currentUser]);\n\n // --- Actions: stable references, read latest state via impl ref ---\n\n // Live implementation of every action. Rebuilt each render so closures\n // capture latest state, but the exposed `actions` proxy stays stable.\n const actionsImplRef = useRef<AuthActionsValue>(null as unknown as AuthActionsValue);\n\n const loadUserData = async (forceRefresh = false) => {\n try {\n if (!sessionManager.hasValidSession()) return;\n if (!forceRefresh && currentUser) return;\n\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);\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 login = async (params: LoginParams): Promise<LoginResponse> => {\n const { username, password, tenantSlug: targetSlug, redirectPath } = params;\n\n let resolvedTenantId = tenant?.id;\n let targetTenantSlug = tenantSlug;\n\n if (targetSlug) {\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 const shouldSwitch = targetSlug && targetSlug !== tenantSlug;\n\n sessionManager.setTokens({\n accessToken: loginResponse.accessToken,\n refreshToken: loginResponse.refreshToken,\n expiresIn: loginResponse.expiresIn,\n });\n\n if (loginResponse.user) {\n sessionManager.setUser(loginResponse.user);\n setCurrentUser(loginResponse.user);\n\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 if (loginResponse.tenants && loginResponse.tenants.length > 0) {\n setUserTenants(loginResponse.tenants);\n writeUserTenants(loginResponse.tenants);\n }\n\n const hasTenant = loginResponse.user?.tenantId !== null;\n\n if (shouldSwitch && targetTenantSlug) {\n switchTenant(targetTenantSlug, { redirectPath });\n return loginResponse;\n }\n\n if (redirectPath && redirectPath !== window.location.pathname) {\n switchTenant(targetTenantSlug || tenantSlug || '', { redirectPath });\n return loginResponse;\n }\n\n if (!hasTenant && loginResponse.tenants && loginResponse.tenants.length > 0) {\n const autoSwitch = params.autoSwitch !== false && config.autoSwitchSingleTenant !== false;\n\n if (loginResponse.tenants.length === 1 && autoSwitch) {\n const singleTenant = loginResponse.tenants[0];\n switchTenant(singleTenant.subdomain, { redirectPath });\n return loginResponse;\n } else if (loginResponse.tenants.length > 1 && config.onTenantSelectionRequired) {\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 return authApiService.signup({\n email,\n phoneNumber,\n name,\n password,\n tenantId: tenantId ?? tenant?.id,\n lastName,\n appId,\n });\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 return authApiService.signupTenantAdmin({\n email,\n phoneNumber,\n name,\n password,\n tenantName,\n appId,\n lastName,\n });\n };\n\n const changePassword = async (params: ChangePasswordParams): Promise<void> => {\n await authApiService.changePassword(params);\n };\n\n const requestPasswordReset = async (params: RequestPasswordResetParams): Promise<void> => {\n const { email, tenantId } = params;\n const resolvedTenantId = tenantId ?? tenant?.id;\n if (!resolvedTenantId) {\n throw new Error('tenantId is required for password reset');\n }\n await authApiService.requestPasswordReset({ email, tenantId: resolvedTenantId });\n };\n\n const confirmPasswordReset = async (params: ConfirmPasswordResetParams): Promise<void> => {\n await authApiService.confirmPasswordReset(params);\n };\n\n const sendMagicLink = async (params: SendMagicLinkParams): Promise<MagicLinkResponse> => {\n const { email, frontendUrl, name, lastName, tenantId } = params;\n const resolvedTenantId = tenantId ?? tenant?.id;\n if (!resolvedTenantId) {\n throw new Error('tenantId is required for magic link authentication');\n }\n return authApiService.sendMagicLink({\n email,\n tenantId: resolvedTenantId,\n frontendUrl,\n name,\n lastName,\n appId,\n });\n };\n\n const verifyMagicLink = async (\n params: VerifyMagicLinkParams\n ): Promise<VerifyMagicLinkResponse> => {\n const { token, email, tenantSlug: targetSlug } = params;\n\n let resolvedTenantId = tenant?.id;\n let targetTenantSlug = tenantSlug;\n\n if (targetSlug) {\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 const shouldSwitch = targetSlug && targetSlug !== tenantSlug;\n\n sessionManager.setTokens({\n accessToken: verifyResponse.accessToken,\n refreshToken: verifyResponse.refreshToken,\n expiresIn: verifyResponse.expiresIn,\n });\n\n if (verifyResponse.user) {\n sessionManager.setUser(verifyResponse.user);\n setCurrentUser(verifyResponse.user);\n\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 magic link:', error);\n }\n }\n }\n\n if (shouldSwitch && targetTenantSlug && targetTenantSlug !== tenantSlug) {\n switchTenant(targetTenantSlug);\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 setUserTenants([]);\n clearUserTenants();\n };\n\n const setTokens = (tokens: { accessToken: string; refreshToken: string; expiresIn: number }) => {\n sessionManager.setTokens(tokens);\n };\n\n const hasValidSession = () => sessionManager.hasValidSession();\n\n const clearSession = () => {\n sessionManager.clearSession();\n setCurrentUser(null);\n setUserError(null);\n };\n\n const refreshRoles = async () => {\n if (!appId) return;\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 switchToTenant = async (\n tenantId: string,\n options?: { redirectPath?: string }\n ): Promise<void> => {\n const { redirectPath } = options || {};\n\n const tokens = sessionManager.getTokens();\n if (!tokens?.refreshToken) {\n throw new Error('No refresh token available for tenant switch');\n }\n\n const response = await authApiService.switchTenant({\n refreshToken: tokens.refreshToken,\n tenantId,\n });\n\n sessionManager.setTokens({\n accessToken: response.accessToken,\n refreshToken: tokens.refreshToken,\n expiresIn: response.expiresIn,\n });\n\n setCurrentUser(response.user);\n sessionManager.setUser(response.user);\n\n const targetTenant = userTenants.find(t => t.id === tenantId);\n if (targetTenant) {\n switchTenant(targetTenant.subdomain, { redirectPath });\n }\n };\n\n const refreshUserTenants = async (): Promise<UserTenantMembership[]> => {\n const tenants = await authApiService.getUserTenants();\n setUserTenants(tenants);\n writeUserTenants(tenants);\n return tenants;\n };\n\n // Refresh the live action impl on every render — closures capture latest state\n actionsImplRef.current = {\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 loadUserData,\n refreshUser: () => loadUserData(),\n refreshRoles,\n switchToTenant,\n refreshUserTenants,\n };\n\n // Stable proxy — same reference across every render. Delegates to\n // the live impl ref so each call sees the freshest closures.\n const actions = useMemo<AuthActionsValue>(\n () => ({\n login: params => actionsImplRef.current.login(params),\n signup: params => actionsImplRef.current.signup(params),\n signupTenantAdmin: params => actionsImplRef.current.signupTenantAdmin(params),\n sendMagicLink: params => actionsImplRef.current.sendMagicLink(params),\n verifyMagicLink: params => actionsImplRef.current.verifyMagicLink(params),\n changePassword: params => actionsImplRef.current.changePassword(params),\n requestPasswordReset: params => actionsImplRef.current.requestPasswordReset(params),\n confirmPasswordReset: params => actionsImplRef.current.confirmPasswordReset(params),\n refreshToken: () => actionsImplRef.current.refreshToken(),\n logout: () => actionsImplRef.current.logout(),\n setTokens: tokens => actionsImplRef.current.setTokens(tokens),\n hasValidSession: () => actionsImplRef.current.hasValidSession(),\n clearSession: () => actionsImplRef.current.clearSession(),\n loadUserData: forceRefresh => actionsImplRef.current.loadUserData(forceRefresh),\n refreshUser: () => actionsImplRef.current.refreshUser(),\n refreshRoles: () => actionsImplRef.current.refreshRoles(),\n switchToTenant: (tenantId, options) =>\n actionsImplRef.current.switchToTenant(tenantId, options),\n refreshUserTenants: () => actionsImplRef.current.refreshUserTenants(),\n }),\n []\n );\n\n // --- State value with permission helpers ---\n\n const stateValue = useMemo<AuthStateValue>(() => {\n const hasPermission = (permission: string | Permission): boolean => {\n if (!userPermissions || userPermissions.length === 0) return false;\n if (typeof permission === 'string') return userPermissions.includes(permission);\n return userPermissions.includes(`${permission.resource}.${permission.action}`);\n };\n\n return {\n isAuthenticated,\n isAuthInitializing: !isAuthReady,\n isAuthReady,\n currentUser,\n isUserLoading,\n userError,\n userRole,\n userPermissions,\n availableRoles,\n rolesLoading,\n userTenants,\n hasTenantContext,\n sessionManager,\n authenticatedHttpService,\n hasPermission,\n hasAnyPermission: permissions => permissions.some(p => hasPermission(p)),\n hasAllPermissions: permissions => permissions.every(p => hasPermission(p)),\n getUserPermissionStrings: () => userPermissions || [],\n };\n }, [\n isAuthenticated,\n isAuthReady,\n currentUser,\n isUserLoading,\n userError,\n userRole,\n userPermissions,\n availableRoles,\n rolesLoading,\n userTenants,\n hasTenantContext,\n sessionManager,\n authenticatedHttpService,\n ]);\n\n // --- Effects ---\n\n useEffect(() => {\n if (config.initialRoles || !appId) return;\n\n let cancelled = false;\n setRolesLoading(true);\n roleApiService\n .getRolesByApp(appId)\n .then(({ roles }) => {\n if (!cancelled) setAvailableRoles(roles);\n })\n .catch(error => {\n if (process.env.NODE_ENV === 'development') {\n console.error('[AuthProvider] Failed to fetch roles:', error);\n }\n })\n .finally(() => {\n if (!cancelled) setRolesLoading(false);\n });\n\n return () => {\n cancelled = true;\n };\n }, [appId, config.initialRoles, roleApiService]);\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 (!sessionManager.hasValidSession() && sessionManager.getTokens()?.refreshToken) {\n await sessionManager.waitForPendingRefresh();\n }\n if (cancelled) return;\n\n if (\n !sessionManager.hasValidSession() &&\n !sessionManager.getTokens() &&\n config.enableCookieSession\n ) {\n await sessionManager.attemptCookieSessionRestore();\n if (cancelled) return;\n }\n\n const user = sessionManager.getUser();\n if (user && sessionManager.hasValidSession()) {\n setCurrentUser(user);\n }\n setIsRestoringSession(false);\n };\n init();\n\n return () => {\n cancelled = true;\n };\n }, [sessionManager, config.enableCookieSession]);\n\n // Auto-load user data if we have tokens but no currentUser\n useEffect(() => {\n if (!currentUser && !isUserLoading && !userError && sessionManager.hasValidSession()) {\n actionsImplRef.current\n .loadUserData()\n .catch(() => {\n // Silent fail - error already logged in loadUserData\n })\n .finally(() => {\n setIsRestoringSession(false);\n });\n } else {\n setIsRestoringSession(false);\n }\n }, [currentUser, isUserLoading, userError, sessionManager]);\n\n return (\n <AuthActionsContext.Provider value={actions}>\n <AuthStateContext.Provider value={stateValue}>{children}</AuthStateContext.Provider>\n </AuthActionsContext.Provider>\n );\n}\n\n/** Fine-grained subscription to reactive auth state. */\nexport function useAuthState(): AuthStateValue {\n const state = useContext(AuthStateContext);\n if (!state) {\n throw new Error('useAuthState must be used within an AuthProvider');\n }\n return state;\n}\n\n/**\n * Fine-grained subscription to stable auth actions.\n * The returned object has a stable reference — subscribers never re-render\n * when auth state changes.\n */\nexport function useAuthActions(): AuthActionsValue {\n const actions = useContext(AuthActionsContext);\n if (!actions) {\n throw new Error('useAuthActions must be used within an AuthProvider');\n }\n return actions;\n}\n\n/**\n * Backward-compatible hook that exposes state + actions together.\n * Prefer useAuthState() or useAuthActions() for fine-grained re-render control.\n */\nexport function useAuth(): AuthContextValue {\n const state = useContext(AuthStateContext);\n const actions = useContext(AuthActionsContext);\n if (!state || !actions) {\n throw new Error('useAuth must be used within an AuthProvider');\n }\n return useMemo(() => ({ ...state, ...actions }), [state, actions]);\n}\n\n/** Optional variant of useAuth — returns null when not inside a provider. */\nexport function useAuthOptional(): AuthContextValue | null {\n const state = useContext(AuthStateContext);\n const actions = useContext(AuthActionsContext);\n return useMemo(() => {\n if (!state || !actions) return null;\n return { ...state, ...actions };\n }, [state, actions]);\n}\n","import { HttpService } from './HttpService';\nimport type {\n ApiResponse,\n FeatureFlagItem,\n FeatureFlagValueResponse,\n FeatureFlag,\n CreateFeatureFlagRequest,\n PaginationParams,\n} from '../types/api';\nimport { buildPaginationQuery } from '../utils/query';\n\nexport class FeatureFlagApiService {\n constructor(private httpService: HttpService) {}\n\n async createFeatureFlag(request: CreateFeatureFlagRequest): Promise<FeatureFlag> {\n const response = await this.httpService.post<ApiResponse<FeatureFlag>>(\n '/feature-flags/',\n request\n );\n return response.data;\n }\n\n async getFeatureFlags(\n params?: PaginationParams\n ): Promise<{ featureFlags: FeatureFlag[]; meta: any }> {\n const response = await this.httpService.get<ApiResponse<FeatureFlag[]>>(\n `/feature-flags/${buildPaginationQuery(params)}`\n );\n return { featureFlags: response.data, meta: response.meta };\n }\n\n async getFeatureFlagById(id: string): Promise<FeatureFlag> {\n const response = await this.httpService.get<ApiResponse<FeatureFlag>>(`/feature-flags/${id}`);\n return response.data;\n }\n\n async updateFeatureFlag(\n id: string,\n request: Partial<CreateFeatureFlagRequest>\n ): Promise<FeatureFlag> {\n const response = await this.httpService.put<ApiResponse<FeatureFlag>>(\n `/feature-flags/${id}`,\n request\n );\n return response.data;\n }\n\n async deleteFeatureFlag(id: string): Promise<void> {\n await this.httpService.delete<void>(`/feature-flags/${id}`);\n }\n\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 query = buildPaginationQuery({ tenantId, appId });\n const response = await this.httpService.get<ApiResponse<FeatureFlagItem[]>>(\n `/tenant-feature-flags${query}`,\n { headers: { 'X-Tenant-ID': tenantId }, skipAuth: true }\n );\n return response.data;\n }\n\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 query = buildPaginationQuery({ tenantId, appId });\n const response = await this.httpService.get<ApiResponse<FeatureFlagValueResponse>>(\n `/tenant-feature-flags/${flagKey}${query}`,\n { headers: { 'X-Tenant-ID': tenantId }, skipAuth: true }\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 type {\n Subscription,\n CreateSubscriptionRequest,\n ApiResponse,\n TenantSubscriptionFeatures,\n} from '../types/api';\n\nexport class SubscriptionApiService {\n constructor(private httpService: HttpService) {}\n\n async createSubscription(request: CreateSubscriptionRequest): Promise<Subscription> {\n const response = await this.httpService.post<ApiResponse<Subscription>>(\n '/subscriptions/',\n request\n );\n return response.data;\n }\n\n async getSubscriptionById(id: string): Promise<Subscription> {\n const response = await this.httpService.get<ApiResponse<Subscription>>(\n `/subscriptions/subscriptions/${id}`\n );\n return response.data;\n }\n\n async updateSubscription(\n id: string,\n request: Partial<CreateSubscriptionRequest>\n ): Promise<Subscription> {\n const response = await this.httpService.put<ApiResponse<Subscription>>(\n `/subscriptions/${id}`,\n request\n );\n return response.data;\n }\n\n async changeSubscriptionPlan(subscriptionId: string, planId: string): Promise<Subscription> {\n const response = await this.httpService.put<ApiResponse<Subscription>>(\n `/subscriptions/${subscriptionId}/plan`,\n { planId }\n );\n return response.data;\n }\n\n async getTenantSubscriptionFeatures(tenantId: string): Promise<TenantSubscriptionFeatures> {\n const response = await this.httpService.get<ApiResponse<TenantSubscriptionFeatures>>(\n `/subscriptions/tenants/${tenantId}/subscription-features`,\n { skipAuth: true }\n );\n return response.data;\n }\n\n async processPayment(subscriptionId: string, paymentData: any): Promise<any> {\n const response = await this.httpService.post<ApiResponse<any>>(\n `/subscriptions/${subscriptionId}/process-payment`,\n paymentData\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\nconst DEFAULT_ROUTING_CONTEXT: RoutingContextValue = {\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\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 return useContext(RoutingContext) ?? DEFAULT_ROUTING_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 { useState, useCallback } from 'react';\n\nexport interface UseAuthFormOptions<TResult> {\n submit: () => Promise<TResult>;\n defaultErrorMessage: string;\n validate?: () => boolean;\n onSuccess?: (result: TResult) => void;\n onError?: (errorMessage: string) => void;\n}\n\nexport interface UseAuthFormReturn<TResult, TField extends string> {\n loading: boolean;\n error: string;\n setError: (message: string) => void;\n fieldErrors: Partial<Record<TField, boolean>>;\n setFieldError: (field: TField, value: boolean) => void;\n clearFieldError: (field: TField) => void;\n resetErrors: () => void;\n handleSubmit: (e?: React.FormEvent) => Promise<TResult | undefined>;\n}\n\n/**\n * Unified form state + submission handler shared across auth forms.\n * Provides loading/error state, field-level error tracking, and a handleSubmit\n * that runs validate → submit → success/error callbacks.\n */\nexport function useAuthForm<TResult = unknown, TField extends string = string>(\n options: UseAuthFormOptions<TResult>\n): UseAuthFormReturn<TResult, TField> {\n const { submit, defaultErrorMessage, validate, onSuccess, onError } = options;\n\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState('');\n const [fieldErrors, setFieldErrors] = useState<Partial<Record<TField, boolean>>>({});\n\n const setFieldError = useCallback((field: TField, value: boolean) => {\n setFieldErrors(prev => ({ ...prev, [field]: value }));\n }, []);\n\n const clearFieldError = useCallback((field: TField) => {\n setFieldErrors(prev => {\n if (!prev[field]) return prev;\n const next = { ...prev };\n delete next[field];\n return next;\n });\n }, []);\n\n const resetErrors = useCallback(() => {\n setError('');\n setFieldErrors({});\n }, []);\n\n const handleSubmit = useCallback(\n async (e?: React.FormEvent) => {\n if (e) e.preventDefault();\n if (validate && !validate()) return undefined;\n\n setLoading(true);\n setError('');\n\n try {\n const result = await submit();\n onSuccess?.(result);\n return result;\n } catch (err) {\n const message = err instanceof Error ? err.message : defaultErrorMessage;\n setError(message);\n onError?.(message);\n return undefined;\n } finally {\n setLoading(false);\n }\n },\n [submit, validate, defaultErrorMessage, onSuccess, onError]\n );\n\n return {\n loading,\n error,\n setError,\n fieldErrors,\n setFieldError,\n clearFieldError,\n resetErrors,\n handleSubmit,\n };\n}\n","import React from 'react';\n\nexport interface AuthFormBaseStyles {\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 successText?: React.CSSProperties;\n hintText?: React.CSSProperties;\n description?: React.CSSProperties;\n verifyingContainer?: React.CSSProperties;\n verifyingText?: React.CSSProperties;\n toggleContainer?: React.CSSProperties;\n toggleLink?: React.CSSProperties;\n subtitle?: React.CSSProperties;\n modeSwitchDivider?: React.CSSProperties;\n}\n\nexport const baseFormStyles: Required<AuthFormBaseStyles> = {\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 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 hintText: {\n fontSize: '0.875rem',\n color: '#6b7280',\n textAlign: 'center',\n margin: '0.5rem 0',\n },\n description: {\n fontSize: '0.875rem',\n color: '#6b7280',\n textAlign: 'center',\n marginBottom: '1.5rem',\n lineHeight: '1.5',\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 subtitle: {\n fontSize: '0.875rem',\n textAlign: 'center',\n marginBottom: '1.5rem',\n color: '#6b7280',\n lineHeight: '1.4',\n },\n modeSwitchDivider: {\n margin: '0 0.5rem',\n color: '#6b7280',\n },\n};\n\n/**\n * Build a merged form styles object that uses `baseFormStyles` as the base,\n * overrides the button background color, then layers user overrides on top.\n */\nexport function buildFormStyles(\n buttonBackgroundColor: string,\n userStyles?: Partial<AuthFormBaseStyles>\n): Required<AuthFormBaseStyles> {\n return {\n ...baseFormStyles,\n ...userStyles,\n button: {\n ...baseFormStyles.button,\n backgroundColor: buttonBackgroundColor,\n ...(userStyles?.button || {}),\n },\n };\n}\n\n// --- Shared password toggle icons (SVG) ---\n\nexport const EyeIcon = (): React.ReactElement =>\n React.createElement(\n 'svg',\n {\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 React.createElement('path', { d: 'M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z' }),\n React.createElement('circle', { cx: '12', cy: '12', r: '3' })\n );\n\nexport const EyeOffIcon = (): React.ReactElement =>\n React.createElement(\n 'svg',\n {\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 React.createElement('path', {\n 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 }),\n React.createElement('line', { x1: '1', y1: '1', x2: '23', y2: '23' })\n );\n","import React, { useState } from 'react';\nimport { useAuth } from '../providers/AuthProvider';\nimport { useAuthForm } from '../hooks/useAuthForm';\nimport { AuthFormBaseStyles, buildFormStyles, EyeIcon, EyeOffIcon } from './authFormShared';\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 type LoginFormStyles = AuthFormBaseStyles;\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\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\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\n const { login } = useAuth();\n\n const mergedCopy = { ...defaultCopy, ...copy };\n const mergedStyles = buildFormStyles('#3b82f6', styles);\n const mergedIcons = { ...defaultIcons, ...icons };\n\n type Field = 'username' | 'password';\n const form = useAuthForm<unknown, Field>({\n defaultErrorMessage: mergedCopy.errorMessage,\n validate: () => {\n const missing: Field[] = [];\n if (!username.trim()) missing.push('username');\n if (!password.trim()) missing.push('password');\n missing.forEach(f => form.setFieldError(f, true));\n return missing.length === 0;\n },\n submit: () => login({ username, password }),\n onSuccess,\n onError,\n });\n\n const getInputStyle = (field: Field) => ({\n ...mergedStyles.input,\n ...(form.fieldErrors[field] ? mergedStyles.inputError : {}),\n });\n\n const isDisabled = !username || !password || form.loading;\n const buttonStyle = {\n ...mergedStyles.button,\n ...(form.loading ? mergedStyles.buttonLoading : {}),\n ...(isDisabled ? 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={form.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 form.clearFieldError('username');\n }}\n placeholder={mergedCopy.usernamePlaceholder}\n style={getInputStyle('username')}\n disabled={form.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 form.clearFieldError('password');\n }}\n placeholder={mergedCopy.passwordPlaceholder}\n style={{ ...getInputStyle('password'), ...mergedStyles.inputWithIcon }}\n disabled={form.loading}\n />\n <button\n type=\"button\"\n onClick={() => setShowPassword(!showPassword)}\n style={mergedStyles.passwordToggle}\n disabled={form.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={isDisabled} style={buttonStyle}>\n {form.loading ? mergedCopy.loadingText : mergedCopy.submitButton}\n </button>\n\n {form.error && <div style={mergedStyles.errorText}>{form.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 { useState } from 'react';\nimport { useAuth } from '../providers/AuthProvider';\nimport { useTenantOptional } from '../providers/TenantProvider';\nimport { useAuthForm } from '../hooks/useAuthForm';\nimport { AuthFormBaseStyles, buildFormStyles } from './authFormShared';\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 type SignupFormStyles = AuthFormBaseStyles;\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\ntype SignupField = 'name' | 'email' | 'phoneNumber' | 'password' | 'confirmPassword' | 'tenantName';\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\n const { signup, signupTenantAdmin } = useAuth();\n const tenant = useTenantOptional()?.tenant ?? null;\n\n const mergedCopy = { ...defaultCopy, ...copy };\n const mergedStyles = buildFormStyles('#10b981', styles);\n\n const isFormValid =\n !!name &&\n (!!email || !!phoneNumber) &&\n !!password &&\n !!confirmPassword &&\n (signupType === 'user' || !!tenantName);\n\n const form = useAuthForm<unknown, SignupField>({\n defaultErrorMessage: mergedCopy.errorMessage,\n validate: () => {\n const missing: SignupField[] = [];\n if (!name.trim()) missing.push('name');\n if (!email.trim() && !phoneNumber.trim()) {\n missing.push('email');\n missing.push('phoneNumber');\n }\n if (!password.trim()) missing.push('password');\n if (!confirmPassword.trim()) missing.push('confirmPassword');\n if (signupType === 'tenant' && !tenantName.trim()) missing.push('tenantName');\n\n missing.forEach(f => form.setFieldError(f, true));\n if (missing.length > 0) return false;\n\n if (password !== confirmPassword) {\n form.setError(mergedCopy.passwordMismatchError);\n form.setFieldError('confirmPassword', true);\n return false;\n }\n\n if (signupType === 'user' && !tenant?.id) {\n form.setError(mergedCopy.tenantNotFoundError);\n return false;\n }\n\n return true;\n },\n submit: async () => {\n if (signupType === 'tenant') {\n return signupTenantAdmin({\n email: email || undefined,\n phoneNumber: phoneNumber || undefined,\n name,\n password,\n tenantName,\n lastName: lastName || undefined,\n });\n }\n return 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,\n onError,\n });\n\n const getInputStyle = (field: SignupField) => ({\n ...mergedStyles.input,\n ...(form.fieldErrors[field] ? mergedStyles.inputError : {}),\n });\n\n const isDisabled = !isFormValid || form.loading;\n const buttonStyle = {\n ...mergedStyles.button,\n ...(form.loading ? mergedStyles.buttonLoading : {}),\n ...(isDisabled ? mergedStyles.buttonDisabled : {}),\n };\n\n const onEmailOrPhoneChange = () => {\n form.clearFieldError('email');\n form.clearFieldError('phoneNumber');\n };\n\n return (\n <div className={className} style={mergedStyles.container}>\n <h2 style={mergedStyles.title}>{mergedCopy.title}</h2>\n\n <form onSubmit={form.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 form.clearFieldError('name');\n }}\n placeholder={mergedCopy.namePlaceholder}\n style={getInputStyle('name')}\n disabled={form.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={form.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 onEmailOrPhoneChange();\n }}\n placeholder={mergedCopy.emailPlaceholder}\n style={getInputStyle('email')}\n disabled={form.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 onEmailOrPhoneChange();\n }}\n placeholder={mergedCopy.phoneNumberPlaceholder}\n style={getInputStyle('phoneNumber')}\n disabled={form.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 form.clearFieldError('password');\n }}\n placeholder={mergedCopy.passwordPlaceholder}\n style={getInputStyle('password')}\n disabled={form.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 form.clearFieldError('confirmPassword');\n if (form.error === mergedCopy.passwordMismatchError) {\n form.setError('');\n }\n }}\n placeholder={mergedCopy.confirmPasswordPlaceholder}\n style={getInputStyle('confirmPassword')}\n disabled={form.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 form.clearFieldError('tenantName');\n }}\n placeholder={mergedCopy.tenantNamePlaceholder}\n style={getInputStyle('tenantName')}\n disabled={form.loading}\n />\n </div>\n )}\n\n <button type=\"submit\" disabled={isDisabled} style={buttonStyle}>\n {form.loading ? mergedCopy.loadingText : mergedCopy.submitButton}\n </button>\n\n {form.error && <div style={mergedStyles.errorText}>{form.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 { useState, useEffect } from 'react';\nimport { useAuth } from '../providers/AuthProvider';\nimport { useTenantOptional } from '../providers/TenantProvider';\nimport { useAuthForm } from '../hooks/useAuthForm';\nimport { AuthFormBaseStyles, buildFormStyles } from './authFormShared';\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 type MagicLinkFormStyles = AuthFormBaseStyles;\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 verifyToken?: string;\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\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 [verifying, setVerifying] = useState(false);\n const [success, setSuccess] = useState('');\n const [showNameFields, setShowNameFields] = useState(false);\n\n const { sendMagicLink, verifyMagicLink } = useAuth();\n const tenant = useTenantOptional()?.tenant ?? null;\n\n const mergedCopy = { ...defaultCopy, ...copy };\n const mergedStyles = buildFormStyles('#3b82f6', styles);\n\n type Field = 'email' | 'name';\n const form = useAuthForm<unknown, Field>({\n defaultErrorMessage: mergedCopy.errorMessage,\n validate: () => {\n const missing: Field[] = [];\n if (!email.trim()) missing.push('email');\n if (showNameFields && !name.trim()) missing.push('name');\n missing.forEach(f => form.setFieldError(f, true));\n if (missing.length > 0) return false;\n if (!tenant?.id) {\n form.setError(mergedCopy.tenantNotFoundError);\n return false;\n }\n return true;\n },\n submit: async () => {\n setSuccess('');\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 return result;\n },\n onSuccess,\n onError,\n });\n\n useEffect(() => {\n if (!verifyToken) return;\n\n const run = async () => {\n if (!tenant || !email) {\n form.setError(mergedCopy.missingTenantOrEmailError);\n return;\n }\n setVerifying(true);\n form.setError('');\n try {\n const result = await verifyMagicLink({ token: verifyToken, email });\n onSuccess?.(result);\n } catch (err) {\n const message = err instanceof Error ? err.message : 'Failed to verify magic link';\n form.setError(message);\n onError?.(message);\n } finally {\n setVerifying(false);\n }\n };\n\n run();\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [verifyToken]);\n\n const getInputStyle = (field: Field) => ({\n ...mergedStyles.input,\n ...(form.fieldErrors[field] ? mergedStyles.inputError : {}),\n });\n\n const isDisabled = !email || form.loading || verifying;\n const buttonStyle = {\n ...mergedStyles.button,\n ...(form.loading || verifying ? mergedStyles.buttonLoading : {}),\n ...(isDisabled ? 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={form.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 form.clearFieldError('email');\n }}\n placeholder={mergedCopy.emailPlaceholder}\n style={getInputStyle('email')}\n disabled={form.loading || verifying}\n />\n </div>\n\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 form.clearFieldError('name');\n }}\n placeholder={mergedCopy.namePlaceholder}\n style={getInputStyle('name')}\n disabled={form.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={form.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={isDisabled} style={buttonStyle}>\n {form.loading ? mergedCopy.loadingText : mergedCopy.submitButton}\n </button>\n\n {form.error && <div style={mergedStyles.errorText}>{form.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 (dedup handled at service level)\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 { useState } from 'react';\nimport { useAuth } from '../providers/AuthProvider';\nimport { useTenantOptional } from '../providers/TenantProvider';\nimport { useAuthForm } from '../hooks/useAuthForm';\nimport { AuthFormBaseStyles, buildFormStyles } from './authFormShared';\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 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 type PasswordRecoveryFormStyles = AuthFormBaseStyles;\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\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 [success, setSuccess] = useState('');\n\n const { requestPasswordReset, confirmPasswordReset } = useAuth();\n const tenant = useTenantOptional()?.tenant ?? null;\n\n const mergedCopy = { ...defaultCopy, ...copy };\n const mergedStyles = buildFormStyles('#f59e0b', styles);\n\n type RequestField = 'email';\n const requestForm = useAuthForm<void, RequestField>({\n defaultErrorMessage: mergedCopy.errorMessage,\n validate: () => {\n if (!email.trim()) {\n requestForm.setFieldError('email', true);\n return false;\n }\n if (!tenant?.id) {\n requestForm.setError(mergedCopy.tenantNotFoundError);\n return false;\n }\n return true;\n },\n submit: async () => {\n setSuccess('');\n await requestPasswordReset({ email, tenantId: tenant!.id });\n setSuccess(mergedCopy.successMessage);\n },\n onSuccess: () => onSuccess?.(),\n onError,\n });\n\n type ResetField = 'token' | 'newPassword' | 'confirmPassword';\n const resetForm = useAuthForm<void, ResetField>({\n defaultErrorMessage: mergedCopy.errorMessage,\n validate: () => {\n const missing: ResetField[] = [];\n if (!token.trim()) missing.push('token');\n if (!newPassword.trim()) missing.push('newPassword');\n if (!confirmPassword.trim()) missing.push('confirmPassword');\n missing.forEach(f => resetForm.setFieldError(f, true));\n if (missing.length > 0) return false;\n\n if (newPassword !== confirmPassword) {\n resetForm.setError(mergedCopy.passwordMismatchError);\n resetForm.setFieldError('confirmPassword', true);\n return false;\n }\n return true;\n },\n submit: async () => {\n setSuccess('');\n await confirmPasswordReset({ token, newPassword });\n setSuccess(mergedCopy.resetSuccessMessage);\n },\n onSuccess: () => onSuccess?.(),\n onError,\n });\n\n if (mode === 'reset') {\n const getInputStyle = (field: ResetField) => ({\n ...mergedStyles.input,\n ...(resetForm.fieldErrors[field] ? mergedStyles.inputError : {}),\n });\n\n const isFormValid = !!token && !!newPassword && !!confirmPassword;\n const isDisabled = !isFormValid || resetForm.loading;\n const buttonStyle = {\n ...mergedStyles.button,\n ...(resetForm.loading ? mergedStyles.buttonLoading : {}),\n ...(isDisabled ? mergedStyles.buttonDisabled : {}),\n };\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={resetForm.handleSubmit} 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 resetForm.clearFieldError('token');\n }}\n placeholder={mergedCopy.tokenPlaceholder}\n style={getInputStyle('token')}\n disabled={resetForm.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 resetForm.clearFieldError('newPassword');\n }}\n placeholder={mergedCopy.newPasswordPlaceholder}\n style={getInputStyle('newPassword')}\n disabled={resetForm.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 resetForm.clearFieldError('confirmPassword');\n if (resetForm.error === mergedCopy.passwordMismatchError) {\n resetForm.setError('');\n }\n }}\n placeholder={mergedCopy.confirmPasswordPlaceholder}\n style={getInputStyle('confirmPassword')}\n disabled={resetForm.loading}\n />\n </div>\n\n <button type=\"submit\" disabled={isDisabled} style={buttonStyle}>\n {resetForm.loading ? mergedCopy.resetLoadingText : mergedCopy.resetSubmitButton}\n </button>\n\n {resetForm.error && <div style={mergedStyles.errorText}>{resetForm.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 getInputStyle = (field: RequestField) => ({\n ...mergedStyles.input,\n ...(requestForm.fieldErrors[field] ? mergedStyles.inputError : {}),\n });\n\n const isDisabled = !email || requestForm.loading;\n const buttonStyle = {\n ...mergedStyles.button,\n ...(requestForm.loading ? mergedStyles.buttonLoading : {}),\n ...(isDisabled ? mergedStyles.buttonDisabled : {}),\n };\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={requestForm.handleSubmit} 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 requestForm.clearFieldError('email');\n }}\n placeholder={mergedCopy.emailPlaceholder}\n style={getInputStyle('email')}\n disabled={requestForm.loading}\n />\n </div>\n\n <button type=\"submit\" disabled={isDisabled} style={buttonStyle}>\n {requestForm.loading ? mergedCopy.loadingText : mergedCopy.submitButton}\n </button>\n\n {requestForm.error && <div style={mergedStyles.errorText}>{requestForm.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 type {\n Permission,\n CreatePermissionRequest,\n ApiResponse,\n PaginationParams,\n} from '../types/api';\nimport { buildPaginationQuery } from '../utils/query';\n\nexport class PermissionApiService {\n constructor(private httpService: HttpService) {}\n\n async createPermission(request: CreatePermissionRequest): Promise<Permission> {\n const response = await this.httpService.post<ApiResponse<Permission>>('/permissions/', request);\n return response.data;\n }\n\n async getPermissions(\n params?: PaginationParams\n ): Promise<{ permissions: Permission[]; meta: any }> {\n const response = await this.httpService.get<ApiResponse<Permission[]>>(\n `/permissions/${buildPaginationQuery(params)}`\n );\n return { permissions: response.data, meta: response.meta };\n }\n\n async getPermissionById(id: string): Promise<Permission> {\n const response = await this.httpService.get<ApiResponse<Permission>>(`/permissions/${id}`);\n return response.data;\n }\n\n async updatePermission(\n id: string,\n request: Partial<CreatePermissionRequest>\n ): Promise<Permission> {\n const response = await this.httpService.put<ApiResponse<Permission>>(\n `/permissions/${id}`,\n request\n );\n return response.data;\n }\n\n async deletePermission(id: string): Promise<void> {\n await this.httpService.delete<void>(`/permissions/${id}`);\n }\n\n async getAppPermissions(\n appId: string,\n params?: PaginationParams\n ): Promise<{ permissions: Permission[]; meta: any }> {\n const response = await this.httpService.get<ApiResponse<Permission[]>>(\n `/permissions/apps/${appId}${buildPaginationQuery(params)}`,\n { skipAuth: true }\n );\n return { permissions: response.data, meta: response.meta };\n }\n}\n","import { HttpService } from './HttpService';\nimport type {\n SubscriptionPlan,\n CreateSubscriptionPlanRequest,\n ApiResponse,\n PaginationParams,\n} from '../types/api';\nimport { buildPaginationQuery } from '../utils/query';\n\nexport class SubscriptionPlanApiService {\n constructor(private httpService: HttpService) {}\n\n async createSubscriptionPlan(request: CreateSubscriptionPlanRequest): Promise<SubscriptionPlan> {\n const response = await this.httpService.post<ApiResponse<SubscriptionPlan>>(\n '/subscription-plans/',\n request\n );\n return response.data;\n }\n\n async getSubscriptionPlans(\n params?: PaginationParams & { appId?: string }\n ): Promise<{ plans: SubscriptionPlan[]; meta: any }> {\n const response = await this.httpService.get<ApiResponse<SubscriptionPlan[]>>(\n `/subscription-plans/${buildPaginationQuery(params)}`\n );\n return { plans: response.data, meta: response.meta };\n }\n\n async getSubscriptionPlanById(id: string): Promise<SubscriptionPlan> {\n const response = await this.httpService.get<ApiResponse<SubscriptionPlan>>(\n `/subscription-plans/${id}`\n );\n return response.data;\n }\n\n async updateSubscriptionPlan(\n id: string,\n request: Partial<CreateSubscriptionPlanRequest>\n ): Promise<SubscriptionPlan> {\n const response = await this.httpService.put<ApiResponse<SubscriptionPlan>>(\n `/subscription-plans/${id}`,\n request\n );\n return response.data;\n }\n\n async deleteSubscriptionPlan(id: string): Promise<void> {\n await this.httpService.delete<void>(`/subscription-plans/${id}`);\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","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","buildPaginationQuery","params","qp","key","value","str","AppApiService","httpService","request","id","appId","planId","schema","defaultSettings","AppContext","createContext","DEFAULT_CACHE_TTL","AppProvider","config","children","cacheEnabled","_a","cacheTtl","_b","cacheStorageKey","_c","appInfo","setAppInfo","useState","cached","parsed","isAppLoading","setIsAppLoading","appError","setAppError","appInfoRef","useRef","loadApp","useCallback","bypassCache","appData","cacheData","err","backgroundRefresh","contextValue","useMemo","useEffect","useApp","context","useContext","useAppOptional","useApi","SessionExpiredError","reason","message","defaultMessages","TokenRefreshTimeoutError","timeoutMs","TokenRefreshError","attempts","lastError","_SessionManager","existing","instance","storageKey","stored","token","parts","payload","claim","tokens","expiresAt","tokenData","currentData","refreshToken","expiresIn","tokenType","resolvedExpiresAt","delay","gen","resolve","reject","idx","e","newTokens","newAccessToken","queue","entry","attempt","backoff","freshTokens","currentRefreshToken","deviceId","refreshBody","networkError","errorMessage","body","refreshResponse","user","expiredError","ms","SessionManager","AuthApiService","pending","promise","RoleApiService","roleId","userId","UserApiService","TenantApiService","slug","detectSubdomainTenant","hostname","baseDomain","baseDomainLower","currentHost","subdomain","detectSelectorTenant","search","selectorParam","localStorage","urlTenant","detectTenantSlug","location","tenantMode","fixedTenantSlug","buildTenantHostname","targetTenantSlug","currentHostname","TenantContext","TenantProvider","detectTenant","tenantSlug","setTenantSlug","cacheConfig","tenant","setTenant","isTenantLoading","setIsTenantLoading","tenantError","setTenantError","settings","setSettings","isSettingsLoading","setIsSettingsLoading","settingsError","setSettingsError","detected","settingsSchema","loadTenant","tenantInfo","loadSettings","tenantSettings","refreshSettings","validateSettings","settingsToValidate","errors","fieldSchema","expectedType","actualType","switchTenant","mode","redirectPath","newHostname","targetPath","urlParams","newUrl","useTenant","useTenantOptional","useTenantSettings","useSettings","useTenantInfo","retryTenant","USER_TENANTS_STORAGE_KEY","readUserTenants","writeUserTenants","tenants","clearUserTenants","AuthStateContext","AuthActionsContext","AuthProvider","appContext","tenantContext","availableRoles","setAvailableRoles","rolesLoading","setRolesLoading","currentUser","setCurrentUser","isUserLoading","setIsUserLoading","userError","setUserError","userTenants","setUserTenants","initRef","isRestoringSession","setIsRestoringSession","isAuthReady","authenticatedHttpService","service","authApiService","userApiService","roleApiService","userRole","role","userPermissions","isAuthenticated","hasTenantContext","actionsImplRef","loadUserData","forceRefresh","userData","login","username","password","targetSlug","resolvedTenantId","loginResponse","shouldSwitch","hasTenant","autoSwitch","singleTenant","signup","email","phoneNumber","name","lastName","tenantId","signupTenantAdmin","tenantName","changePassword","requestPasswordReset","confirmPasswordReset","sendMagicLink","frontendUrl","verifyMagicLink","verifyResponse","logout","setTokens","hasValidSession","clearSession","refreshRoles","roles","switchToTenant","targetTenant","t","refreshUserTenants","actions","stateValue","hasPermission","permission","permissions","p","cancelled","jsx","useAuthState","state","useAuthActions","useAuth","useAuthOptional","FeatureFlagApiService","query","flagKey","FeatureFlagContext","FeatureFlagProvider","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","DEFAULT_ROUTING_CONTEXT","RoutingProvider","zoneRoots","presets","useRouting","useRoutingOptional","DefaultFallback","jsxs","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","perms","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","useAuthForm","submit","defaultErrorMessage","validate","onSuccess","onError","fieldErrors","setFieldErrors","setFieldError","field","prev","clearFieldError","next","resetErrors","handleSubmit","result","baseFormStyles","buildFormStyles","buttonBackgroundColor","userStyles","EyeIcon","React","EyeOffIcon","defaultIcons","defaultCopy","LoginForm","copy","styles","icons","onForgotPassword","onSignupClick","onMagicLinkClick","showForgotPassword","showSignupLink","showMagicLinkOption","className","setUsername","setPassword","showPassword","setShowPassword","mergedCopy","mergedStyles","mergedIcons","form","missing","getInputStyle","isDisabled","buttonStyle","SignupForm","signupType","onLoginClick","showLoginLink","setName","setLastName","setEmail","setPhoneNumber","confirmPassword","setConfirmPassword","setTenantName","isFormValid","onEmailOrPhoneChange","MagicLinkForm","showTraditionalLinks","verifyToken","verifying","setVerifying","success","setSuccess","showNameFields","setShowNameFields","finalFrontendUrl","defaultStyles","LoadingIcon","SuccessIcon","ErrorIcon","MagicLinkVerify","onRetry","onBackToLogin","propToken","propEmail","propAppId","propTenantSlug","autoRedirectDelay","setState","getUrlParams","handleVerification","handleRetry","handleBackToLogin","renderContent","base","PasswordRecoveryForm","initialToken","onModeChange","setToken","newPassword","setNewPassword","requestForm","resetForm","DefaultLoadingFallback","DefaultErrorFallback","retry","AppLoader","errorFallback","requireTenant","retryApp","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","DEFAULT_RETURN_TO_PARAM","RETURN_TO_SESSION_KEY","RETURN_TO_LOCAL_KEY","useZoneNavigation","navigate","useNavigate","searchParams","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/utils/query.ts","../src/services/AppApiService.ts","../src/providers/AppProvider.tsx","../src/errors/SessionErrors.ts","../src/utils/jwt.ts","../src/utils/configValidation.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/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/hooks/useAuthForm.ts","../src/components/authFormShared.ts","../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/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","/**\n * Serialize params into a query-string suffix (including the leading '?').\n * Returns an empty string if no params are provided or none are set.\n */\nexport function buildPaginationQuery(params?: object): string {\n if (!params) return '';\n const qp = new URLSearchParams();\n for (const [key, value] of Object.entries(params)) {\n if (value !== undefined && value !== null && value !== '') {\n qp.append(key, String(value));\n }\n }\n const str = qp.toString();\n return str ? `?${str}` : '';\n}\n","import { HttpService } from './HttpService';\nimport type {\n ApiResponse,\n App,\n CreateAppRequest,\n PublicAppInfo,\n PaginationParams,\n} from '../types/api';\nimport { buildPaginationQuery } from '../utils/query';\n\nexport class AppApiService {\n constructor(private httpService: HttpService) {}\n\n async createApp(request: CreateAppRequest): Promise<App> {\n const response = await this.httpService.post<ApiResponse<App>>('/apps/', request);\n return response.data;\n }\n\n async getApps(params?: PaginationParams): Promise<{ apps: App[]; meta: any }> {\n const response = await this.httpService.get<ApiResponse<App[]>>(\n `/apps/${buildPaginationQuery(params)}`\n );\n return { apps: response.data, meta: response.meta };\n }\n\n async getAppById(id: string): Promise<App> {\n const response = await this.httpService.get<ApiResponse<App>>(`/apps/${id}`);\n return response.data;\n }\n\n async updateApp(id: string, request: Partial<CreateAppRequest>): Promise<App> {\n const response = await this.httpService.put<ApiResponse<App>>(`/apps/${id}`, request);\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 skipAuth: true,\n });\n return response.data;\n }\n\n async setDefaultSubscriptionPlan(appId: string, planId: string): Promise<App> {\n const response = await this.httpService.put<ApiResponse<App>>(\n `/apps/${appId}/default-subscription-plan`,\n { planId }\n );\n return response.data;\n }\n\n async updateSettingsSchema(appId: string, schema: any, defaultSettings: any): Promise<App> {\n const response = await this.httpService.put<ApiResponse<App>>(\n `/apps/${appId}/settings-schema`,\n { schema, defaultSettings }\n );\n return response.data;\n }\n\n async exportConfig(appId: string): Promise<any> {\n const response = await this.httpService.get<ApiResponse<any>>(`/apps/${appId}/export-config`);\n return response.data;\n }\n}\n","import {\n createContext,\n useContext,\n useMemo,\n ReactNode,\n useState,\n useEffect,\n useCallback,\n useRef,\n} from 'react';\nimport { HttpService } from '../services/HttpService';\nimport { AppApiService } from '../services/AppApiService';\nimport type { PublicAppInfo } from '../types/api';\n\ninterface CachedAppInfo {\n data: PublicAppInfo;\n timestamp: number;\n appId: string;\n}\n\nexport interface AppConfig {\n baseUrl: string;\n appId: string;\n cache?: {\n enabled?: boolean;\n ttl?: number;\n storageKey?: string;\n };\n}\n\ninterface AppContextValue {\n appId: string;\n baseUrl: string;\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\nconst DEFAULT_CACHE_TTL = 5 * 60 * 1000;\n\nexport function AppProvider({ config, children }: AppProviderProps) {\n const { appId, baseUrl } = config;\n const cacheEnabled = config.cache?.enabled ?? true;\n const cacheTtl = config.cache?.ttl ?? DEFAULT_CACHE_TTL;\n const cacheStorageKey = config.cache?.storageKey ?? `app_cache_${appId}`;\n\n const [appInfo, setAppInfo] = useState<PublicAppInfo | null>(() => {\n if (!cacheEnabled) return null;\n try {\n const cached = localStorage.getItem(cacheStorageKey);\n if (!cached) return null;\n const parsed: CachedAppInfo = JSON.parse(cached);\n if (Date.now() - parsed.timestamp < cacheTtl && parsed.appId === appId) {\n return parsed.data;\n }\n localStorage.removeItem(cacheStorageKey);\n return null;\n } catch {\n return null;\n }\n });\n\n const [isAppLoading, setIsAppLoading] = useState(!appInfo);\n const [appError, setAppError] = useState<Error | null>(null);\n\n const appInfoRef = useRef(appInfo);\n appInfoRef.current = appInfo;\n\n const loadApp = useCallback(\n async (bypassCache = false) => {\n if (!bypassCache && cacheEnabled && appInfoRef.current) return;\n\n try {\n setIsAppLoading(true);\n setAppError(null);\n\n const appApi = new AppApiService(new HttpService(baseUrl));\n const appData = await appApi.getPublicAppInfo(appId);\n setAppInfo(appData);\n\n if (cacheEnabled) {\n try {\n const cacheData: CachedAppInfo = {\n data: appData,\n timestamp: Date.now(),\n appId,\n };\n localStorage.setItem(cacheStorageKey, 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 [baseUrl, appId, cacheEnabled, cacheStorageKey]\n );\n\n const backgroundRefresh = useCallback(async () => {\n if (!cacheEnabled || !appInfoRef.current) return;\n\n try {\n const cached = localStorage.getItem(cacheStorageKey);\n if (!cached) return;\n\n const parsed: CachedAppInfo = JSON.parse(cached);\n if (Date.now() - parsed.timestamp <= cacheTtl * 0.5) return;\n\n const appApi = new AppApiService(new HttpService(baseUrl));\n const appData = await appApi.getPublicAppInfo(appId);\n setAppInfo(appData);\n\n const cacheData: CachedAppInfo = {\n data: appData,\n timestamp: Date.now(),\n appId,\n };\n localStorage.setItem(cacheStorageKey, JSON.stringify(cacheData));\n } catch (error) {\n if (process.env.NODE_ENV === 'development') {\n console.warn('[AppProvider] Background app refresh failed:', error);\n }\n }\n }, [baseUrl, appId, cacheEnabled, cacheTtl, cacheStorageKey]);\n\n const contextValue = useMemo<AppContextValue>(\n () => ({\n appId,\n baseUrl,\n appInfo,\n isAppLoading,\n appError,\n retryApp: () => {\n loadApp(true);\n },\n }),\n [appId, baseUrl, appInfo, isAppLoading, appError, loadApp]\n );\n\n useEffect(() => {\n if (appInfoRef.current) {\n backgroundRefresh();\n } else {\n loadApp();\n }\n // Run once on mount — loadApp/backgroundRefresh read the latest state via ref\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\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\nexport function useAppOptional(): AppContextValue | null {\n return useContext(AppContext);\n}\n\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\n/**\n * Thrown synchronously when a configuration value is invalid (wrong type,\n * out of range, dangerous scheme). Raised at construction time so callers\n * get immediate feedback.\n */\nexport class ConfigurationError extends Error {\n public readonly field: string;\n public readonly received: unknown;\n\n constructor(field: string, received: unknown, reason: string) {\n super(`Invalid configuration \"${field}\": ${reason} (received: ${describeValue(received)})`);\n this.name = 'ConfigurationError';\n this.field = field;\n this.received = received;\n }\n}\n\nfunction describeValue(v: unknown): string {\n if (v === undefined) return 'undefined';\n try {\n const json = JSON.stringify(v);\n if (json === undefined) return typeof v;\n return json.length > 50 ? `${json.slice(0, 47)}...` : json;\n } catch {\n return typeof v;\n }\n}\n","/**\n * Shared JWT helpers. Both SessionManager and configValidation use these to\n * avoid duplicating base64url decoding logic.\n */\n\nexport interface DecodedJwt {\n header: Record<string, unknown>;\n payload: Record<string, unknown>;\n}\n\nfunction decodeSegment(segment: string): Record<string, unknown> {\n return JSON.parse(atob(segment.replace(/-/g, '+').replace(/_/g, '/')));\n}\n\n/**\n * Decode a JWT into its header and payload objects. Returns null for anything\n * that is not a structurally valid 3-segment JWT with parseable base64url\n * JSON header and payload. Does NOT verify the signature.\n */\nexport function decodeJwt(token: string): DecodedJwt | null {\n const parts = token.split('.');\n if (parts.length !== 3) return null;\n try {\n return { header: decodeSegment(parts[0]), payload: decodeSegment(parts[1]) };\n } catch {\n return null;\n }\n}\n\n/**\n * Extract the `exp` claim (as ms since epoch) from a JWT access token, or\n * undefined if the token isn't a JWT or has no exp claim.\n */\nexport function extractJwtExpiry(token: string): number | undefined {\n const decoded = decodeJwt(token);\n const exp = decoded?.payload.exp;\n return typeof exp === 'number' ? exp * 1000 : undefined;\n}\n\n/**\n * Extract a string claim from a JWT payload, or undefined if the token isn't\n * a JWT, the claim is missing, or the claim value isn't a string.\n */\nexport function extractJwtClaim(token: string, claim: string): string | undefined {\n const value = decodeJwt(token)?.payload[claim];\n return typeof value === 'string' ? value : undefined;\n}\n","import { ConfigurationError } from '../errors/SessionErrors';\nimport { decodeJwt } from './jwt';\n\nconst DANGEROUS_SCHEMES = /^(javascript|data|vbscript|file):/i;\nconst SAFE_SCHEMES = /^https?:\\/\\//i;\n\n/**\n * Validate a baseUrl string. Empty/undefined is allowed (standalone mode and\n * inheritance from AppProvider both rely on this), but any non-empty value\n * must start with http:// or https://. Dangerous schemes (javascript:, data:,\n * vbscript:, file:) are rejected with a typed error.\n */\nexport function validateBaseUrl(baseUrl: unknown, field = 'baseUrl'): void {\n if (baseUrl === undefined || baseUrl === null || baseUrl === '') return;\n if (typeof baseUrl !== 'string') {\n throw new ConfigurationError(field, baseUrl, 'must be a string');\n }\n if (DANGEROUS_SCHEMES.test(baseUrl)) {\n throw new ConfigurationError(field, baseUrl, 'dangerous URL scheme is not allowed');\n }\n if (!SAFE_SCHEMES.test(baseUrl)) {\n throw new ConfigurationError(field, baseUrl, 'must start with http:// or https://');\n }\n}\n\n/**\n * Validate a numeric config option. Rejects NaN, Infinity, non-numbers, and\n * values outside [min, max]. `min` defaults to 0 (non-negative); pass a\n * positive `min` to require strictly positive.\n */\nexport function validateNumber(\n field: string,\n value: unknown,\n options: { min?: number; max?: number } = {}\n): void {\n if (value === undefined) return;\n if (typeof value !== 'number' || !Number.isFinite(value)) {\n throw new ConfigurationError(field, value, 'must be a finite number');\n }\n const { min = 0, max = Number.MAX_SAFE_INTEGER } = options;\n if (value < min) {\n throw new ConfigurationError(field, value, `must be >= ${min}`);\n }\n if (value > max) {\n throw new ConfigurationError(field, value, `must be <= ${max}`);\n }\n}\n\n/**\n * Validate a boolean config option. Rejects strings like \"true\"/\"false\" that\n * callers sometimes pass by accident.\n */\nexport function validateBoolean(field: string, value: unknown): void {\n if (value === undefined) return;\n if (typeof value !== 'boolean') {\n throw new ConfigurationError(field, value, 'must be a boolean');\n }\n}\n\n/**\n * Validate an access token's structural shape. Tokens with dots are treated\n * as JWTs and must decode cleanly; tokens without dots are treated as opaque\n * and accepted as-is.\n */\nexport function validateTokenShape(token: unknown, field = 'accessToken'): void {\n if (typeof token !== 'string' || token.length === 0) {\n throw new ConfigurationError(field, token, 'must be a non-empty string');\n }\n\n if (!token.includes('.')) return;\n\n const parts = token.split('.');\n if (parts.length !== 3) {\n throw new ConfigurationError(\n field,\n `<${parts.length}-segment token>`,\n 'JWT must have exactly 3 segments (header.payload.signature)'\n );\n }\n\n if (decodeJwt(token) === null) {\n throw new ConfigurationError(\n field,\n '<malformed JWT>',\n 'JWT header or payload is not valid base64url-encoded JSON'\n );\n }\n}\n\n/**\n * Validate an expiresIn value (seconds until access token expires).\n * Must be a finite positive number if provided. Rejects 0, NaN, Infinity,\n * negative, and non-number inputs.\n */\nexport function validateExpiresIn(value: unknown): void {\n if (value === undefined) return;\n if (typeof value !== 'number' || !Number.isFinite(value) || value <= 0) {\n throw new ConfigurationError('expiresIn', value, 'must be a finite positive number (seconds)');\n }\n}\n\n/**\n * Validate an absolute expiresAt timestamp (ms since epoch). Same rules as\n * expiresIn: finite, positive.\n */\nexport function validateExpiresAt(value: unknown): void {\n if (value === undefined) return;\n if (typeof value !== 'number' || !Number.isFinite(value) || value <= 0) {\n throw new ConfigurationError(\n 'expiresAt',\n value,\n 'must be a finite positive timestamp (ms since epoch)'\n );\n }\n}\n","import {\n SessionExpiredError,\n TokenRefreshTimeoutError,\n TokenRefreshError,\n ConfigurationError,\n} from '../errors/SessionErrors';\nimport {\n validateBaseUrl,\n validateNumber,\n validateBoolean,\n validateTokenShape,\n validateExpiresIn,\n validateExpiresAt,\n} from '../utils/configValidation';\nimport { decodeJwt, extractJwtClaim, extractJwtExpiry } from '../utils/jwt';\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 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 enableCookieSession?: boolean; // When true, sends credentials: 'include' on refresh calls for cross-subdomain cookie auth\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 return config.storageKey || '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 private enableCookieSession: boolean;\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 private consecutiveBackgroundFailures = 0;\n private static readonly MAX_BACKGROUND_FAILURES = 3;\n\n constructor(config: SessionConfig = {}) {\n SessionManager.validateConfig(config);\n\n this.storageKey = config.storageKey || 'auth_tokens';\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 this.enableCookieSession = config.enableCookieSession ?? false;\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 this.attachVisibilityListener();\n this.scheduleProactiveRefresh();\n }\n\n private static validateConfig(config: SessionConfig): void {\n validateBaseUrl(config.baseUrl);\n validateBoolean('enableCookieSession', config.enableCookieSession);\n validateBoolean('autoRefresh', config.autoRefresh);\n validateNumber('refreshThreshold', config.refreshThreshold, { min: 0 });\n validateNumber('proactiveRefreshMargin', config.proactiveRefreshMargin, { min: 0 });\n validateNumber('refreshQueueTimeout', config.refreshQueueTimeout, { min: 1 });\n validateNumber('maxRefreshRetries', config.maxRefreshRetries, { min: 0 });\n validateNumber('retryBackoffBase', config.retryBackoffBase, { min: 1 });\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 if (config.enableCookieSession !== undefined)\n this.enableCookieSession = config.enableCookieSession;\n }\n\n // --- Storage helpers ---\n\n // Lazily allocated when localStorage rejects a write. Non-null ⇒ fallback active.\n private memoryStore: Map<string, string> | null = null;\n\n private createTokenStorage(storageKey: string): TokenStorage {\n if (!SessionManager.probeLocalStorage()) {\n this.activateMemoryFallback();\n }\n\n return {\n get: () => {\n const stored = this.storageGet(storageKey);\n if (!stored) return null;\n try {\n return JSON.parse(stored);\n } catch {\n return null;\n }\n },\n set: (data: any) => {\n this.storageSet(storageKey, JSON.stringify(data));\n },\n clear: () => {\n this.storageRemove(storageKey);\n },\n };\n }\n\n private static probeLocalStorage(): boolean {\n if (typeof localStorage === 'undefined') return false;\n try {\n localStorage.setItem('__sm_probe__', '1');\n localStorage.removeItem('__sm_probe__');\n return true;\n } catch {\n return false;\n }\n }\n\n private activateMemoryFallback(): Map<string, string> {\n if (!this.memoryStore) {\n this.memoryStore = new Map();\n if (process.env.NODE_ENV === 'development') {\n console.warn(\n '[SessionManager] localStorage unavailable — falling back to in-memory session. Cross-tab and reload persistence are lost.'\n );\n }\n }\n return this.memoryStore;\n }\n\n private storageGet(key: string): string | null {\n if (this.memoryStore) return this.memoryStore.get(key) ?? null;\n try {\n return localStorage.getItem(key);\n } catch {\n return this.activateMemoryFallback().get(key) ?? null;\n }\n }\n\n private storageSet(key: string, value: string): void {\n if (this.memoryStore) {\n this.memoryStore.set(key, value);\n return;\n }\n try {\n localStorage.setItem(key, value);\n } catch {\n // Mid-session storage failure (quota, policy change). Switch to memory\n // so the current session keeps working, then replay the write.\n this.activateMemoryFallback().set(key, value);\n }\n }\n\n private storageRemove(key: string): void {\n if (this.memoryStore) {\n this.memoryStore.delete(key);\n return;\n }\n try {\n localStorage.removeItem(key);\n } catch {\n // Best-effort: remove failures are ignored; the next write will fall back.\n }\n }\n\n // --- Token CRUD ---\n\n setTokens(tokens: TokenData): void {\n validateTokenShape(tokens.accessToken, 'accessToken');\n // Refresh tokens are often opaque, so we only reject obvious type errors.\n // Empty string remains allowed for legacy cookie-session flows.\n if (tokens.refreshToken !== undefined && typeof tokens.refreshToken !== 'string') {\n throw new ConfigurationError('refreshToken', tokens.refreshToken, 'must be a string');\n }\n validateExpiresIn(tokens.expiresIn);\n validateExpiresAt(tokens.expiresAt);\n\n const expiresAt =\n tokens.expiresAt ||\n (tokens.expiresIn ? Date.now() + tokens.expiresIn * 1000 : undefined) ||\n 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 || 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 // --- Visibility-aware rescheduling ---\n\n private visibilityListener: (() => void) | null = null;\n\n private attachVisibilityListener(): void {\n if (this.visibilityListener) return;\n if (typeof document === 'undefined' || typeof document.addEventListener !== 'function') {\n return;\n }\n // Browsers throttle/drop setTimeout while the tab is hidden, and clock\n // skew (NTP, laptop sleep) can make a pending timer miss its window.\n // Rescheduling on visibility recomputes from current Date.now().\n this.visibilityListener = () => {\n if (document.visibilityState !== 'visible' || this.isDestroyed) return;\n this.scheduleProactiveRefresh();\n };\n try {\n document.addEventListener('visibilitychange', this.visibilityListener);\n } catch {\n this.visibilityListener = null;\n }\n }\n\n private detachVisibilityListener(): void {\n if (this.visibilityListener && typeof document !== 'undefined') {\n try {\n document.removeEventListener('visibilitychange', this.visibilityListener);\n } catch {\n // ignore\n }\n }\n this.visibilityListener = null;\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 — reset circuit breaker\n this.consecutiveBackgroundFailures = 0;\n })\n .catch(error => {\n if (error instanceof SessionExpiredError) {\n // Fatal — already handled by startRefreshAndResolveQueue\n } else if (this.sessionGeneration === gen) {\n // Circuit breaker: after MAX_BACKGROUND_FAILURES consecutive transient\n // failures, treat as fatal to prevent infinite retry loops\n this.consecutiveBackgroundFailures++;\n if (this.consecutiveBackgroundFailures >= SessionManager.MAX_BACKGROUND_FAILURES) {\n if (process.env.NODE_ENV === 'development') {\n console.error(\n `[SessionManager] Background refresh failed ${this.consecutiveBackgroundFailures} consecutive times — expiring session`\n );\n }\n this.consecutiveBackgroundFailures = 0;\n this.handleSessionExpired(\n new SessionExpiredError('token_invalid', 'Background refresh failed repeatedly')\n );\n return;\n }\n\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 /**\n * Attempt to restore a session using a backend-set HttpOnly cookie.\n * Sends a refresh request with credentials: 'include' but without a refresh token in the body.\n * If the backend responds with tokens (cookie carried the refresh token), stores them and returns true.\n * If it fails (no cookie, expired, etc.), returns false — this is a normal outcome, not an error.\n *\n * Only works when enableCookieSession is true.\n */\n async attemptCookieSessionRestore(): Promise<boolean> {\n if (!this.enableCookieSession || !this.baseUrl) return false;\n\n const url = `${this.baseUrl}/auth/refresh`;\n\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({}),\n credentials: 'include',\n });\n\n if (!response.ok) return false;\n\n const data = await response.json();\n if (!data.accessToken) return false;\n\n this.setTokens({\n accessToken: data.accessToken,\n refreshToken: data.refreshToken || '',\n expiresIn: data.expiresIn,\n });\n\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 // Use Web Locks API to coordinate refresh across browser tabs.\n // Without this, multiple tabs sharing localStorage can send the same\n // refresh token simultaneously, triggering \"reuse detected\" on servers\n // that rotate refresh tokens.\n if (typeof navigator !== 'undefined' && navigator.locks) {\n return navigator.locks.request(`session-refresh:${this.storageKey}`, () =>\n this.performTokenRefreshInner(refreshToken, gen)\n );\n }\n return this.performTokenRefreshInner(refreshToken, gen);\n }\n\n private async performTokenRefreshInner(refreshToken: string, gen: number): Promise<void> {\n if (!this.baseUrl) {\n throw new Error('Base URL not configured for token refresh');\n }\n\n // Re-read tokens from storage: another browser tab sharing localStorage\n // may have already refreshed while we were waiting for the lock or retrying.\n const freshTokens = this.getTokens();\n if (\n freshTokens?.accessToken &&\n !this.isTokenExpired(freshTokens) &&\n !this.shouldRefreshToken(freshTokens)\n ) {\n // Another tab already refreshed — the access token in storage is valid.\n // Skip the fetch entirely to avoid sending a stale refresh token\n // (which would trigger \"reuse detected\" on servers with token rotation).\n return;\n }\n\n // Use the freshest refresh token from storage. If another tab rotated it,\n // the old RT we received as parameter is now invalid — use the new one.\n const currentRefreshToken = freshTokens?.refreshToken || refreshToken;\n\n const url = `${this.baseUrl}/auth/refresh`;\n\n // Extract deviceId from the refresh token JWT if present.\n // Some backends require it as a separate body field for refresh.\n const deviceId = extractJwtClaim(currentRefreshToken, 'deviceId');\n const refreshBody: Record<string, string> = { refreshToken: currentRefreshToken };\n if (deviceId) {\n refreshBody.deviceId = deviceId;\n }\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(refreshBody),\n ...(this.enableCookieSession && { credentials: 'include' as RequestCredentials }),\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 // Token reuse / revocation — fatal (server revoked all sessions)\n if (errorMessage.includes('reuse') || errorMessage.includes('revoked')) {\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 || currentRefreshToken,\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 this.detachVisibilityListener();\n const error = new SessionExpiredError('token_invalid', 'SessionManager destroyed');\n this.rejectQueue(error);\n }\n\n // --- JWT helpers ---\n\n getTokenPayload(): JwtPayload | null {\n const token = this.getTokens()?.accessToken;\n if (!token) return null;\n return (decodeJwt(token)?.payload as unknown as JwtPayload) ?? null;\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 // Prevents duplicate verifyMagicLink calls (React StrictMode double-mount)\n private pendingVerifications = new Map<string, Promise<VerifyMagicLinkResponse>>();\n // Prevents duplicate sendMagicLink calls (double-click, StrictMode double-invoke)\n private pendingMagicLinks = new Map<string, Promise<MagicLinkResponse>>();\n\n constructor(private httpService: HttpService) {}\n\n // Public endpoints - no auth required.\n // Must pass `{ skipAuth: true }` so HttpService does NOT call\n // SessionManager.getValidAccessToken (which throws when no tokens exist).\n async login(request: LoginRequest): Promise<LoginResponse> {\n return this.httpService.post<LoginResponse>('/auth/login', request, { skipAuth: true });\n }\n\n async signup(request: SignupRequest): Promise<User> {\n return this.httpService.post<User>('/auth/signup', request, { skipAuth: true });\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 return this.httpService.post<{ user: User; tenant: any }>(\n '/auth/signup/tenant-admin',\n request,\n { skipAuth: true }\n );\n }\n\n async refreshToken(request: RefreshTokenRequest): Promise<RefreshTokenResponse> {\n return this.httpService.post<RefreshTokenResponse>('/auth/refresh', request, {\n skipAuth: true,\n });\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(): Promise<UserTenantMembership[]> {\n return this.httpService.get<UserTenantMembership[]>('/auth/tenants');\n }\n\n async requestPasswordReset(request: { email: string; tenantId: string }): Promise<void> {\n await this.httpService.post<void>('/auth/password-reset/request', request, { skipAuth: true });\n }\n\n async sendMagicLink(request: MagicLinkRequest): Promise<MagicLinkResponse> {\n const key = JSON.stringify([\n request.email,\n request.tenantId,\n request.appId ?? '',\n request.frontendUrl ?? '',\n ]);\n const pending = this.pendingMagicLinks.get(key);\n if (pending) return pending;\n\n const promise = this.httpService\n .post<MagicLinkResponse>('/auth/magic-link/send', request, { skipAuth: true })\n .finally(() => {\n this.pendingMagicLinks.delete(key);\n });\n\n this.pendingMagicLinks.set(key, promise);\n return promise;\n }\n\n async verifyMagicLink(request: VerifyMagicLinkRequest): Promise<VerifyMagicLinkResponse> {\n const key = request.token;\n const pending = this.pendingVerifications.get(key);\n if (pending) return pending;\n\n const promise = this.httpService\n .post<VerifyMagicLinkResponse>('/auth/magic-link/verify', request, { skipAuth: true })\n .finally(() => {\n this.pendingVerifications.delete(key);\n });\n\n this.pendingVerifications.set(key, promise);\n return promise;\n }\n\n async confirmPasswordReset(request: { token: string; newPassword: string }): Promise<void> {\n await this.httpService.post<void>('/auth/password-reset/confirm', request, { skipAuth: true });\n }\n\n async changePassword(request: ChangePasswordRequest): Promise<void> {\n await this.httpService.post<ApiResponse<null>>('/auth/change-password', request);\n }\n}\n","import { HttpService } from './HttpService';\nimport type {\n Role,\n CreateRoleRequest,\n AssignRoleRequest,\n ApiResponse,\n PaginationParams,\n} from '../types/api';\nimport { buildPaginationQuery } from '../utils/query';\n\nexport class RoleApiService {\n constructor(private httpService: HttpService) {}\n\n async createRole(request: CreateRoleRequest): Promise<Role> {\n const response = await this.httpService.post<ApiResponse<Role>>('/roles/', request);\n return response.data;\n }\n\n async getRoleById(id: string): Promise<Role> {\n const response = await this.httpService.get<ApiResponse<Role>>(`/roles/${id}`);\n return response.data;\n }\n\n async updateRole(id: string, request: Partial<CreateRoleRequest>): Promise<Role> {\n const response = await this.httpService.put<ApiResponse<Role>>(`/roles/${id}`, request);\n return response.data;\n }\n\n async deleteRole(id: string): Promise<void> {\n await this.httpService.delete<void>(`/roles/${id}`);\n }\n\n async getRolesByApp(\n appId: string,\n params?: PaginationParams\n ): Promise<{ roles: Role[]; meta: any }> {\n const response = await this.httpService.get<ApiResponse<Role[]>>(\n `/roles/app/${appId}${buildPaginationQuery(params)}`,\n { skipAuth: true }\n );\n return { roles: response.data, meta: response.meta };\n }\n\n async assignRole(roleId: string, request: AssignRoleRequest): Promise<void> {\n await this.httpService.post<ApiResponse<null>>(`/roles/${roleId}/assign`, request);\n }\n\n async revokeRole(roleId: string, request: AssignRoleRequest): Promise<void> {\n await this.httpService.post<ApiResponse<null>>(`/roles/${roleId}/revoke`, request);\n }\n\n async getUserRoles(\n userId: string,\n params?: PaginationParams\n ): Promise<{ roles: Role[]; meta: any }> {\n const response = await this.httpService.get<ApiResponse<Role[]>>(\n `/roles/user/${userId}${buildPaginationQuery(params)}`\n );\n return { roles: response.data, meta: response.meta };\n }\n}\n","import { HttpService } from './HttpService';\nimport type { User, CreateUserRequest, ApiResponse, PaginationParams } from '../types/api';\nimport { buildPaginationQuery } from '../utils/query';\n\nexport class UserApiService {\n constructor(private httpService: HttpService) {}\n\n async createUser(request: CreateUserRequest): Promise<User> {\n const response = await this.httpService.post<ApiResponse<User>>('/users/', request);\n return response.data;\n }\n\n async getUsers(params?: PaginationParams): Promise<{ users: User[]; meta: any }> {\n const response = await this.httpService.get<ApiResponse<User[]>>(\n `/users/${buildPaginationQuery(params)}`\n );\n return { users: response.data, meta: response.meta };\n }\n\n async getUserById(id: string): Promise<User> {\n const response = await this.httpService.get<ApiResponse<User>>(`/users/${id}`);\n return response.data;\n }\n\n async updateUser(id: string, request: Partial<CreateUserRequest>): Promise<User> {\n const response = await this.httpService.put<ApiResponse<User>>(`/users/${id}`, request);\n return response.data;\n }\n\n async deleteUser(id: string): Promise<void> {\n await this.httpService.delete<void>(`/users/${id}`);\n }\n}\n","import { HttpService } from './HttpService';\nimport type {\n Tenant,\n CreateTenantRequest,\n PublicTenantInfo,\n TenantSettings,\n UpdateTenantSettingsRequest,\n ApiResponse,\n PaginationParams,\n} from '../types/api';\nimport { buildPaginationQuery } from '../utils/query';\n\nexport class TenantApiService {\n constructor(\n private httpService: HttpService,\n private appId?: string\n ) {}\n\n async createTenant(request: CreateTenantRequest): Promise<Tenant> {\n const response = await this.httpService.post<ApiResponse<Tenant>>('/tenants/', request);\n return response.data;\n }\n\n async getTenants(params?: PaginationParams): Promise<{ tenants: Tenant[]; meta: any }> {\n const response = await this.httpService.get<ApiResponse<Tenant[]>>(\n `/tenants/${buildPaginationQuery(params)}`\n );\n return { tenants: response.data, meta: response.meta };\n }\n\n async getTenantById(id: string): Promise<Tenant> {\n const response = await this.httpService.get<ApiResponse<Tenant>>(`/tenants/${id}`);\n return response.data;\n }\n\n async updateTenant(id: string, request: Partial<CreateTenantRequest>): Promise<Tenant> {\n const response = await this.httpService.put<ApiResponse<Tenant>>(`/tenants/${id}`, request);\n return response.data;\n }\n\n async adminUpdateTenant(id: string, request: Partial<CreateTenantRequest>): Promise<Tenant> {\n const response = await this.httpService.put<ApiResponse<Tenant>>(\n `/tenants/${id}/admin-update`,\n request\n );\n return response.data;\n }\n\n async getPublicTenantInfo(slug: string): Promise<PublicTenantInfo> {\n const response = await this.httpService.get<ApiResponse<PublicTenantInfo>>(\n `/tenants/${this.appId}/${slug}/public`,\n { skipAuth: true }\n );\n return response.data;\n }\n\n async getTenantSettings(id: string): Promise<TenantSettings> {\n const response = await this.httpService.get<ApiResponse<TenantSettings>>(\n `/tenants/${id}/settings`,\n { skipAuth: true }\n );\n return response.data;\n }\n\n async updateTenantSettings(\n id: string,\n request: UpdateTenantSettingsRequest\n ): Promise<TenantSettings> {\n const response = await this.httpService.put<ApiResponse<TenantSettings>>(\n `/tenants/${id}/settings`,\n request\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","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 type { TenantSettings, JSONSchema, PublicTenantInfo } from '../types/api';\n\n// 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 // 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'; 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 const cacheEnabled = config.cache?.enabled ?? true;\n const cacheTtl = config.cache?.ttl ?? 5 * 60 * 1000;\n const cacheStorageKey = config.cache?.storageKey ?? `tenant_cache_${tenantSlug || 'default'}`;\n const cacheConfig = useMemo(\n () => ({ enabled: cacheEnabled, ttl: cacheTtl, storageKey: cacheStorageKey }),\n [cacheEnabled, cacheTtl, cacheStorageKey]\n );\n\n // 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 // Load tenant info with caching\n const loadTenant = useCallback(\n async (slug: string, bypassCache = false) => {\n if (!bypassCache && cacheConfig.enabled && tenant && tenant.subdomain === slug) {\n return;\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 // 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 // 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 // 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 // Cross-subdomain auth is handled by enableCookieSession — the receiving\n // subdomain restores the session from a parent-domain HttpOnly refresh cookie.\n const switchTenant = useCallback(\n (\n targetTenantSlug: string,\n options?: { mode?: 'navigate' | 'reload'; redirectPath?: string }\n ) => {\n const { mode = 'reload', 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 if (redirectPath) {\n window.location.href = redirectPath;\n }\n return;\n }\n\n localStorage.setItem('tenant', targetTenantSlug);\n\n if (tenantMode === '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 const targetPath = redirectPath || window.location.pathname;\n const url = new URL(`${window.location.protocol}//${newHostname}${targetPath}`);\n\n const currentParams = new URLSearchParams(window.location.search);\n currentParams.forEach((value, key) => {\n url.searchParams.set(key, value);\n });\n\n window.location.href = url.toString();\n } else if (tenantMode === 'selector') {\n const targetPath = redirectPath || window.location.pathname;\n const urlParams = new URLSearchParams(window.location.search);\n urlParams.set(config.selectorParam || 'tenant', targetTenantSlug);\n\n if (mode === 'reload') {\n const newUrl = `${targetPath}?${urlParams.toString()}${window.location.hash}`;\n window.location.href = newUrl;\n } else {\n const newUrl = `${targetPath}?${urlParams.toString()}${window.location.hash}`;\n window.history.pushState({}, '', newUrl);\n setTenantSlug(targetTenantSlug);\n loadTenant(targetTenantSlug);\n }\n }\n },\n [config.tenantMode, config.selectorParam, config.baseDomain, config.fixedTenantSlug, 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 { useAppOptional } from './AppProvider';\nimport { useTenantOptional } from './TenantProvider';\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\nconst USER_TENANTS_STORAGE_KEY = 'userTenants';\n\nfunction readUserTenants(): UserTenantMembership[] {\n try {\n const cached = localStorage.getItem(USER_TENANTS_STORAGE_KEY);\n return cached ? JSON.parse(cached) : [];\n } catch {\n return [];\n }\n}\n\nfunction writeUserTenants(tenants: UserTenantMembership[]): void {\n try {\n localStorage.setItem(USER_TENANTS_STORAGE_KEY, JSON.stringify(tenants));\n } catch {\n // Ignore storage errors\n }\n}\n\nfunction clearUserTenants(): void {\n try {\n localStorage.removeItem(USER_TENANTS_STORAGE_KEY);\n } catch {\n // Ignore storage errors\n }\n}\n\nexport interface AuthConfig {\n /** @deprecated Use onSessionExpired instead */\n onRefreshFailed?: () => void;\n onSessionExpired?: (error: SessionExpiredError) => void;\n initialRoles?: Role[];\n refreshQueueTimeout?: number;\n proactiveRefreshMargin?: number;\n autoSwitchSingleTenant?: boolean;\n onTenantSelectionRequired?: (tenants: UserTenantMembership[]) => void;\n enableCookieSession?: boolean;\n baseUrl?: string;\n appId?: string;\n}\n\n/** Reactive auth state + permission helpers. Subscribe via useAuthState(). */\nexport interface AuthStateValue {\n isAuthenticated: boolean;\n isAuthInitializing: boolean;\n isAuthReady: boolean;\n currentUser: User | null;\n isUserLoading: boolean;\n userError: Error | null;\n userRole: Role | null;\n userPermissions: string[];\n availableRoles: Role[];\n rolesLoading: boolean;\n userTenants: UserTenantMembership[];\n hasTenantContext: boolean;\n sessionManager: SessionManager;\n authenticatedHttpService: HttpService;\n hasPermission: (permission: string | Permission) => boolean;\n hasAnyPermission: (permissions: (string | Permission)[]) => boolean;\n hasAllPermissions: (permissions: (string | Permission)[]) => boolean;\n getUserPermissionStrings: () => string[];\n}\n\n/** Stable auth action methods. Subscribe via useAuthActions() — never re-renders. */\nexport interface AuthActionsValue {\n login: (params: LoginParams) => Promise<LoginResponse>;\n signup: (params: SignupParams) => Promise<User>;\n signupTenantAdmin: (params: SignupTenantAdminParams) => Promise<{ user: User; tenant: any }>;\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 setTokens: (tokens: { accessToken: string; refreshToken: string; expiresIn: number }) => void;\n hasValidSession: () => boolean;\n clearSession: () => void;\n loadUserData: (forceRefresh?: boolean) => Promise<void>;\n refreshUser: () => Promise<void>;\n refreshRoles: () => Promise<void>;\n switchToTenant: (tenantId: string, options?: { redirectPath?: string }) => Promise<void>;\n refreshUserTenants: () => Promise<UserTenantMembership[]>;\n}\n\nexport type AuthContextValue = AuthStateValue & AuthActionsValue;\n\nconst AuthStateContext = createContext<AuthStateValue | null>(null);\nconst AuthActionsContext = createContext<AuthActionsValue | null>(null);\n\ninterface AuthProviderProps {\n config?: AuthConfig;\n children: ReactNode;\n}\n\nexport function AuthProvider({ config = {}, children }: AuthProviderProps) {\n const appContext = useAppOptional();\n const tenantContext = useTenantOptional();\n\n const baseUrl = appContext?.baseUrl ?? config.baseUrl ?? '';\n const appId = appContext?.appId ?? config.appId;\n const tenant = tenantContext?.tenant ?? null;\n const tenantSlug = tenantContext?.tenantSlug ?? null;\n const switchTenant = tenantContext?.switchTenant ?? (() => {});\n\n if (!baseUrl) {\n throw new Error(\n '[AuthProvider] baseUrl is required. Provide it via AppProvider or AuthConfig.baseUrl.'\n );\n }\n\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 const [userTenants, setUserTenants] = useState<UserTenantMembership[]>(() => readUserTenants());\n\n // === SYNCHRONOUS INITIALIZATION ===\n // Marks first-render init so downstream effects can gate on it.\n const initRef = useRef<{ done: boolean }>({ done: false });\n\n if (!initRef.current.done) {\n initRef.current.done = true;\n }\n\n const sessionManager = useMemo(() => {\n return SessionManager.getInstance({\n baseUrl,\n enableCookieSession: config.enableCookieSession,\n refreshQueueTimeout: config.refreshQueueTimeout,\n proactiveRefreshMargin: config.proactiveRefreshMargin,\n onSessionExpired: (error: SessionExpiredError) => {\n setCurrentUser(null);\n setUserError(null);\n setUserTenants([]);\n clearUserTenants();\n if (config.onSessionExpired) {\n config.onSessionExpired(error);\n } else if (config.onRefreshFailed) {\n config.onRefreshFailed();\n }\n },\n });\n }, [\n baseUrl,\n config.enableCookieSession,\n config.refreshQueueTimeout,\n config.proactiveRefreshMargin,\n ]);\n\n // CRITICAL: Initialize isRestoringSession 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 const tokens = sessionManager.getTokens();\n if (tokens) {\n return sessionManager.hasValidSession() || !!tokens.refreshToken;\n }\n if (config.enableCookieSession) return true;\n return false;\n });\n\n const isAuthReady = initRef.current.done && !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 () => new AuthApiService(authenticatedHttpService),\n [authenticatedHttpService]\n );\n\n const userApiService = useMemo(\n () => new UserApiService(authenticatedHttpService),\n [authenticatedHttpService]\n );\n\n const roleApiService = useMemo(\n () => new RoleApiService(authenticatedHttpService),\n [authenticatedHttpService]\n );\n\n const userRole = useMemo(() => {\n return currentUser?.roleId\n ? availableRoles.find(role => role.id === currentUser.roleId) || null\n : null;\n }, [currentUser, availableRoles]);\n\n const userPermissions = useMemo(() => userRole?.permissions || [], [userRole]);\n\n const isAuthenticated = useMemo(\n () => sessionManager.hasValidSession() && currentUser !== null,\n [sessionManager, currentUser]\n );\n\n const hasTenantContext = useMemo(() => currentUser?.tenantId != null, [currentUser]);\n\n // --- Actions: stable references, read latest state via impl ref ---\n\n // Live implementation of every action. Rebuilt each render so closures\n // capture latest state, but the exposed `actions` proxy stays stable.\n const actionsImplRef = useRef<AuthActionsValue>(null as unknown as AuthActionsValue);\n\n const loadUserData = async (forceRefresh = false) => {\n try {\n if (!sessionManager.hasValidSession()) return;\n if (!forceRefresh && currentUser) return;\n\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);\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 login = async (params: LoginParams): Promise<LoginResponse> => {\n const { username, password, tenantSlug: targetSlug, redirectPath } = params;\n\n let resolvedTenantId = tenant?.id;\n let targetTenantSlug = tenantSlug;\n\n if (targetSlug) {\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 const shouldSwitch = targetSlug && targetSlug !== tenantSlug;\n\n sessionManager.setTokens({\n accessToken: loginResponse.accessToken,\n refreshToken: loginResponse.refreshToken,\n expiresIn: loginResponse.expiresIn,\n });\n\n if (loginResponse.user) {\n sessionManager.setUser(loginResponse.user);\n setCurrentUser(loginResponse.user);\n\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 if (loginResponse.tenants && loginResponse.tenants.length > 0) {\n setUserTenants(loginResponse.tenants);\n writeUserTenants(loginResponse.tenants);\n }\n\n const hasTenant = loginResponse.user?.tenantId !== null;\n\n if (shouldSwitch && targetTenantSlug) {\n switchTenant(targetTenantSlug, { redirectPath });\n return loginResponse;\n }\n\n if (redirectPath && redirectPath !== window.location.pathname) {\n switchTenant(targetTenantSlug || tenantSlug || '', { redirectPath });\n return loginResponse;\n }\n\n if (!hasTenant && loginResponse.tenants && loginResponse.tenants.length > 0) {\n const autoSwitch = params.autoSwitch !== false && config.autoSwitchSingleTenant !== false;\n\n if (loginResponse.tenants.length === 1 && autoSwitch) {\n const singleTenant = loginResponse.tenants[0];\n switchTenant(singleTenant.subdomain, { redirectPath });\n return loginResponse;\n } else if (loginResponse.tenants.length > 1 && config.onTenantSelectionRequired) {\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 return authApiService.signup({\n email,\n phoneNumber,\n name,\n password,\n tenantId: tenantId ?? tenant?.id,\n lastName,\n appId,\n });\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 return authApiService.signupTenantAdmin({\n email,\n phoneNumber,\n name,\n password,\n tenantName,\n appId,\n lastName,\n });\n };\n\n const changePassword = async (params: ChangePasswordParams): Promise<void> => {\n await authApiService.changePassword(params);\n };\n\n const requestPasswordReset = async (params: RequestPasswordResetParams): Promise<void> => {\n const { email, tenantId } = params;\n const resolvedTenantId = tenantId ?? tenant?.id;\n if (!resolvedTenantId) {\n throw new Error('tenantId is required for password reset');\n }\n await authApiService.requestPasswordReset({ email, tenantId: resolvedTenantId });\n };\n\n const confirmPasswordReset = async (params: ConfirmPasswordResetParams): Promise<void> => {\n await authApiService.confirmPasswordReset(params);\n };\n\n const sendMagicLink = async (params: SendMagicLinkParams): Promise<MagicLinkResponse> => {\n const { email, frontendUrl, name, lastName, tenantId } = params;\n const resolvedTenantId = tenantId ?? tenant?.id;\n if (!resolvedTenantId) {\n throw new Error('tenantId is required for magic link authentication');\n }\n return authApiService.sendMagicLink({\n email,\n tenantId: resolvedTenantId,\n frontendUrl,\n name,\n lastName,\n appId,\n });\n };\n\n const verifyMagicLink = async (\n params: VerifyMagicLinkParams\n ): Promise<VerifyMagicLinkResponse> => {\n const { token, email, tenantSlug: targetSlug } = params;\n\n let resolvedTenantId = tenant?.id;\n let targetTenantSlug = tenantSlug;\n\n if (targetSlug) {\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 const shouldSwitch = targetSlug && targetSlug !== tenantSlug;\n\n sessionManager.setTokens({\n accessToken: verifyResponse.accessToken,\n refreshToken: verifyResponse.refreshToken,\n expiresIn: verifyResponse.expiresIn,\n });\n\n if (verifyResponse.user) {\n sessionManager.setUser(verifyResponse.user);\n setCurrentUser(verifyResponse.user);\n\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 magic link:', error);\n }\n }\n }\n\n if (shouldSwitch && targetTenantSlug && targetTenantSlug !== tenantSlug) {\n switchTenant(targetTenantSlug);\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 setUserTenants([]);\n clearUserTenants();\n };\n\n const setTokens = (tokens: { accessToken: string; refreshToken: string; expiresIn: number }) => {\n sessionManager.setTokens(tokens);\n };\n\n const hasValidSession = () => sessionManager.hasValidSession();\n\n const clearSession = () => {\n sessionManager.clearSession();\n setCurrentUser(null);\n setUserError(null);\n };\n\n const refreshRoles = async () => {\n if (!appId) return;\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 switchToTenant = async (\n tenantId: string,\n options?: { redirectPath?: string }\n ): Promise<void> => {\n const { redirectPath } = options || {};\n\n const tokens = sessionManager.getTokens();\n if (!tokens?.refreshToken) {\n throw new Error('No refresh token available for tenant switch');\n }\n\n const response = await authApiService.switchTenant({\n refreshToken: tokens.refreshToken,\n tenantId,\n });\n\n sessionManager.setTokens({\n accessToken: response.accessToken,\n refreshToken: tokens.refreshToken,\n expiresIn: response.expiresIn,\n });\n\n setCurrentUser(response.user);\n sessionManager.setUser(response.user);\n\n const targetTenant = userTenants.find(t => t.id === tenantId);\n if (targetTenant) {\n switchTenant(targetTenant.subdomain, { redirectPath });\n }\n };\n\n const refreshUserTenants = async (): Promise<UserTenantMembership[]> => {\n const tenants = await authApiService.getUserTenants();\n setUserTenants(tenants);\n writeUserTenants(tenants);\n return tenants;\n };\n\n // Refresh the live action impl on every render — closures capture latest state\n actionsImplRef.current = {\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 loadUserData,\n refreshUser: () => loadUserData(),\n refreshRoles,\n switchToTenant,\n refreshUserTenants,\n };\n\n // Stable proxy — same reference across every render. Delegates to\n // the live impl ref so each call sees the freshest closures.\n const actions = useMemo<AuthActionsValue>(\n () => ({\n login: params => actionsImplRef.current.login(params),\n signup: params => actionsImplRef.current.signup(params),\n signupTenantAdmin: params => actionsImplRef.current.signupTenantAdmin(params),\n sendMagicLink: params => actionsImplRef.current.sendMagicLink(params),\n verifyMagicLink: params => actionsImplRef.current.verifyMagicLink(params),\n changePassword: params => actionsImplRef.current.changePassword(params),\n requestPasswordReset: params => actionsImplRef.current.requestPasswordReset(params),\n confirmPasswordReset: params => actionsImplRef.current.confirmPasswordReset(params),\n refreshToken: () => actionsImplRef.current.refreshToken(),\n logout: () => actionsImplRef.current.logout(),\n setTokens: tokens => actionsImplRef.current.setTokens(tokens),\n hasValidSession: () => actionsImplRef.current.hasValidSession(),\n clearSession: () => actionsImplRef.current.clearSession(),\n loadUserData: forceRefresh => actionsImplRef.current.loadUserData(forceRefresh),\n refreshUser: () => actionsImplRef.current.refreshUser(),\n refreshRoles: () => actionsImplRef.current.refreshRoles(),\n switchToTenant: (tenantId, options) =>\n actionsImplRef.current.switchToTenant(tenantId, options),\n refreshUserTenants: () => actionsImplRef.current.refreshUserTenants(),\n }),\n []\n );\n\n // --- State value with permission helpers ---\n\n const stateValue = useMemo<AuthStateValue>(() => {\n const hasPermission = (permission: string | Permission): boolean => {\n if (!userPermissions || userPermissions.length === 0) return false;\n if (typeof permission === 'string') return userPermissions.includes(permission);\n return userPermissions.includes(`${permission.resource}.${permission.action}`);\n };\n\n return {\n isAuthenticated,\n isAuthInitializing: !isAuthReady,\n isAuthReady,\n currentUser,\n isUserLoading,\n userError,\n userRole,\n userPermissions,\n availableRoles,\n rolesLoading,\n userTenants,\n hasTenantContext,\n sessionManager,\n authenticatedHttpService,\n hasPermission,\n hasAnyPermission: permissions => permissions.some(p => hasPermission(p)),\n hasAllPermissions: permissions => permissions.every(p => hasPermission(p)),\n getUserPermissionStrings: () => userPermissions || [],\n };\n }, [\n isAuthenticated,\n isAuthReady,\n currentUser,\n isUserLoading,\n userError,\n userRole,\n userPermissions,\n availableRoles,\n rolesLoading,\n userTenants,\n hasTenantContext,\n sessionManager,\n authenticatedHttpService,\n ]);\n\n // --- Effects ---\n\n useEffect(() => {\n if (config.initialRoles || !appId) return;\n\n let cancelled = false;\n setRolesLoading(true);\n roleApiService\n .getRolesByApp(appId)\n .then(({ roles }) => {\n if (!cancelled) setAvailableRoles(roles);\n })\n .catch(error => {\n if (process.env.NODE_ENV === 'development') {\n console.error('[AuthProvider] Failed to fetch roles:', error);\n }\n })\n .finally(() => {\n if (!cancelled) setRolesLoading(false);\n });\n\n return () => {\n cancelled = true;\n };\n }, [appId, config.initialRoles, roleApiService]);\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 (!sessionManager.hasValidSession() && sessionManager.getTokens()?.refreshToken) {\n await sessionManager.waitForPendingRefresh();\n }\n if (cancelled) return;\n\n if (\n !sessionManager.hasValidSession() &&\n !sessionManager.getTokens() &&\n config.enableCookieSession\n ) {\n await sessionManager.attemptCookieSessionRestore();\n if (cancelled) return;\n }\n\n const user = sessionManager.getUser();\n if (user && sessionManager.hasValidSession()) {\n setCurrentUser(user);\n }\n setIsRestoringSession(false);\n };\n init();\n\n return () => {\n cancelled = true;\n };\n }, [sessionManager, config.enableCookieSession]);\n\n // Auto-load user data if we have tokens but no currentUser\n useEffect(() => {\n if (!currentUser && !isUserLoading && !userError && sessionManager.hasValidSession()) {\n actionsImplRef.current\n .loadUserData()\n .catch(() => {\n // Silent fail - error already logged in loadUserData\n })\n .finally(() => {\n setIsRestoringSession(false);\n });\n } else {\n setIsRestoringSession(false);\n }\n }, [currentUser, isUserLoading, userError, sessionManager]);\n\n return (\n <AuthActionsContext.Provider value={actions}>\n <AuthStateContext.Provider value={stateValue}>{children}</AuthStateContext.Provider>\n </AuthActionsContext.Provider>\n );\n}\n\n/** Fine-grained subscription to reactive auth state. */\nexport function useAuthState(): AuthStateValue {\n const state = useContext(AuthStateContext);\n if (!state) {\n throw new Error('useAuthState must be used within an AuthProvider');\n }\n return state;\n}\n\n/**\n * Fine-grained subscription to stable auth actions.\n * The returned object has a stable reference — subscribers never re-render\n * when auth state changes.\n */\nexport function useAuthActions(): AuthActionsValue {\n const actions = useContext(AuthActionsContext);\n if (!actions) {\n throw new Error('useAuthActions must be used within an AuthProvider');\n }\n return actions;\n}\n\n/**\n * Backward-compatible hook that exposes state + actions together.\n * Prefer useAuthState() or useAuthActions() for fine-grained re-render control.\n */\nexport function useAuth(): AuthContextValue {\n const state = useContext(AuthStateContext);\n const actions = useContext(AuthActionsContext);\n if (!state || !actions) {\n throw new Error('useAuth must be used within an AuthProvider');\n }\n return useMemo(() => ({ ...state, ...actions }), [state, actions]);\n}\n\n/** Optional variant of useAuth — returns null when not inside a provider. */\nexport function useAuthOptional(): AuthContextValue | null {\n const state = useContext(AuthStateContext);\n const actions = useContext(AuthActionsContext);\n return useMemo(() => {\n if (!state || !actions) return null;\n return { ...state, ...actions };\n }, [state, actions]);\n}\n","import { HttpService } from './HttpService';\nimport type {\n ApiResponse,\n FeatureFlagItem,\n FeatureFlagValueResponse,\n FeatureFlag,\n CreateFeatureFlagRequest,\n PaginationParams,\n} from '../types/api';\nimport { buildPaginationQuery } from '../utils/query';\n\nexport class FeatureFlagApiService {\n constructor(private httpService: HttpService) {}\n\n async createFeatureFlag(request: CreateFeatureFlagRequest): Promise<FeatureFlag> {\n const response = await this.httpService.post<ApiResponse<FeatureFlag>>(\n '/feature-flags/',\n request\n );\n return response.data;\n }\n\n async getFeatureFlags(\n params?: PaginationParams\n ): Promise<{ featureFlags: FeatureFlag[]; meta: any }> {\n const response = await this.httpService.get<ApiResponse<FeatureFlag[]>>(\n `/feature-flags/${buildPaginationQuery(params)}`\n );\n return { featureFlags: response.data, meta: response.meta };\n }\n\n async getFeatureFlagById(id: string): Promise<FeatureFlag> {\n const response = await this.httpService.get<ApiResponse<FeatureFlag>>(`/feature-flags/${id}`);\n return response.data;\n }\n\n async updateFeatureFlag(\n id: string,\n request: Partial<CreateFeatureFlagRequest>\n ): Promise<FeatureFlag> {\n const response = await this.httpService.put<ApiResponse<FeatureFlag>>(\n `/feature-flags/${id}`,\n request\n );\n return response.data;\n }\n\n async deleteFeatureFlag(id: string): Promise<void> {\n await this.httpService.delete<void>(`/feature-flags/${id}`);\n }\n\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 query = buildPaginationQuery({ tenantId, appId });\n const response = await this.httpService.get<ApiResponse<FeatureFlagItem[]>>(\n `/tenant-feature-flags${query}`,\n { headers: { 'X-Tenant-ID': tenantId }, skipAuth: true }\n );\n return response.data;\n }\n\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 query = buildPaginationQuery({ tenantId, appId });\n const response = await this.httpService.get<ApiResponse<FeatureFlagValueResponse>>(\n `/tenant-feature-flags/${flagKey}${query}`,\n { headers: { 'X-Tenant-ID': tenantId }, skipAuth: true }\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 type {\n Subscription,\n CreateSubscriptionRequest,\n ApiResponse,\n TenantSubscriptionFeatures,\n} from '../types/api';\n\nexport class SubscriptionApiService {\n constructor(private httpService: HttpService) {}\n\n async createSubscription(request: CreateSubscriptionRequest): Promise<Subscription> {\n const response = await this.httpService.post<ApiResponse<Subscription>>(\n '/subscriptions/',\n request\n );\n return response.data;\n }\n\n async getSubscriptionById(id: string): Promise<Subscription> {\n const response = await this.httpService.get<ApiResponse<Subscription>>(\n `/subscriptions/subscriptions/${id}`\n );\n return response.data;\n }\n\n async updateSubscription(\n id: string,\n request: Partial<CreateSubscriptionRequest>\n ): Promise<Subscription> {\n const response = await this.httpService.put<ApiResponse<Subscription>>(\n `/subscriptions/${id}`,\n request\n );\n return response.data;\n }\n\n async changeSubscriptionPlan(subscriptionId: string, planId: string): Promise<Subscription> {\n const response = await this.httpService.put<ApiResponse<Subscription>>(\n `/subscriptions/${subscriptionId}/plan`,\n { planId }\n );\n return response.data;\n }\n\n async getTenantSubscriptionFeatures(tenantId: string): Promise<TenantSubscriptionFeatures> {\n const response = await this.httpService.get<ApiResponse<TenantSubscriptionFeatures>>(\n `/subscriptions/tenants/${tenantId}/subscription-features`,\n { skipAuth: true }\n );\n return response.data;\n }\n\n async processPayment(subscriptionId: string, paymentData: any): Promise<any> {\n const response = await this.httpService.post<ApiResponse<any>>(\n `/subscriptions/${subscriptionId}/process-payment`,\n paymentData\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\nconst DEFAULT_ROUTING_CONTEXT: RoutingContextValue = {\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\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 return useContext(RoutingContext) ?? DEFAULT_ROUTING_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 { useState, useCallback } from 'react';\n\nexport interface UseAuthFormOptions<TResult> {\n submit: () => Promise<TResult>;\n defaultErrorMessage: string;\n validate?: () => boolean;\n onSuccess?: (result: TResult) => void;\n onError?: (errorMessage: string) => void;\n}\n\nexport interface UseAuthFormReturn<TResult, TField extends string> {\n loading: boolean;\n error: string;\n setError: (message: string) => void;\n fieldErrors: Partial<Record<TField, boolean>>;\n setFieldError: (field: TField, value: boolean) => void;\n clearFieldError: (field: TField) => void;\n resetErrors: () => void;\n handleSubmit: (e?: React.FormEvent) => Promise<TResult | undefined>;\n}\n\n/**\n * Unified form state + submission handler shared across auth forms.\n * Provides loading/error state, field-level error tracking, and a handleSubmit\n * that runs validate → submit → success/error callbacks.\n */\nexport function useAuthForm<TResult = unknown, TField extends string = string>(\n options: UseAuthFormOptions<TResult>\n): UseAuthFormReturn<TResult, TField> {\n const { submit, defaultErrorMessage, validate, onSuccess, onError } = options;\n\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState('');\n const [fieldErrors, setFieldErrors] = useState<Partial<Record<TField, boolean>>>({});\n\n const setFieldError = useCallback((field: TField, value: boolean) => {\n setFieldErrors(prev => ({ ...prev, [field]: value }));\n }, []);\n\n const clearFieldError = useCallback((field: TField) => {\n setFieldErrors(prev => {\n if (!prev[field]) return prev;\n const next = { ...prev };\n delete next[field];\n return next;\n });\n }, []);\n\n const resetErrors = useCallback(() => {\n setError('');\n setFieldErrors({});\n }, []);\n\n const handleSubmit = useCallback(\n async (e?: React.FormEvent) => {\n if (e) e.preventDefault();\n if (validate && !validate()) return undefined;\n\n setLoading(true);\n setError('');\n\n try {\n const result = await submit();\n onSuccess?.(result);\n return result;\n } catch (err) {\n const message = err instanceof Error ? err.message : defaultErrorMessage;\n setError(message);\n onError?.(message);\n return undefined;\n } finally {\n setLoading(false);\n }\n },\n [submit, validate, defaultErrorMessage, onSuccess, onError]\n );\n\n return {\n loading,\n error,\n setError,\n fieldErrors,\n setFieldError,\n clearFieldError,\n resetErrors,\n handleSubmit,\n };\n}\n","import React from 'react';\n\nexport interface AuthFormBaseStyles {\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 successText?: React.CSSProperties;\n hintText?: React.CSSProperties;\n description?: React.CSSProperties;\n verifyingContainer?: React.CSSProperties;\n verifyingText?: React.CSSProperties;\n toggleContainer?: React.CSSProperties;\n toggleLink?: React.CSSProperties;\n subtitle?: React.CSSProperties;\n modeSwitchDivider?: React.CSSProperties;\n}\n\nexport const baseFormStyles: Required<AuthFormBaseStyles> = {\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 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 hintText: {\n fontSize: '0.875rem',\n color: '#6b7280',\n textAlign: 'center',\n margin: '0.5rem 0',\n },\n description: {\n fontSize: '0.875rem',\n color: '#6b7280',\n textAlign: 'center',\n marginBottom: '1.5rem',\n lineHeight: '1.5',\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 subtitle: {\n fontSize: '0.875rem',\n textAlign: 'center',\n marginBottom: '1.5rem',\n color: '#6b7280',\n lineHeight: '1.4',\n },\n modeSwitchDivider: {\n margin: '0 0.5rem',\n color: '#6b7280',\n },\n};\n\n/**\n * Build a merged form styles object that uses `baseFormStyles` as the base,\n * overrides the button background color, then layers user overrides on top.\n */\nexport function buildFormStyles(\n buttonBackgroundColor: string,\n userStyles?: Partial<AuthFormBaseStyles>\n): Required<AuthFormBaseStyles> {\n return {\n ...baseFormStyles,\n ...userStyles,\n button: {\n ...baseFormStyles.button,\n backgroundColor: buttonBackgroundColor,\n ...(userStyles?.button || {}),\n },\n };\n}\n\n// --- Shared password toggle icons (SVG) ---\n\nexport const EyeIcon = (): React.ReactElement =>\n React.createElement(\n 'svg',\n {\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 React.createElement('path', { d: 'M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z' }),\n React.createElement('circle', { cx: '12', cy: '12', r: '3' })\n );\n\nexport const EyeOffIcon = (): React.ReactElement =>\n React.createElement(\n 'svg',\n {\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 React.createElement('path', {\n 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 }),\n React.createElement('line', { x1: '1', y1: '1', x2: '23', y2: '23' })\n );\n","import React, { useState } from 'react';\nimport { useAuth } from '../providers/AuthProvider';\nimport { useAuthForm } from '../hooks/useAuthForm';\nimport { AuthFormBaseStyles, buildFormStyles, EyeIcon, EyeOffIcon } from './authFormShared';\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 type LoginFormStyles = AuthFormBaseStyles;\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\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\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\n const { login } = useAuth();\n\n const mergedCopy = { ...defaultCopy, ...copy };\n const mergedStyles = buildFormStyles('#3b82f6', styles);\n const mergedIcons = { ...defaultIcons, ...icons };\n\n type Field = 'username' | 'password';\n const form = useAuthForm<unknown, Field>({\n defaultErrorMessage: mergedCopy.errorMessage,\n validate: () => {\n const missing: Field[] = [];\n if (!username.trim()) missing.push('username');\n if (!password.trim()) missing.push('password');\n missing.forEach(f => form.setFieldError(f, true));\n return missing.length === 0;\n },\n submit: () => login({ username, password }),\n onSuccess,\n onError,\n });\n\n const getInputStyle = (field: Field) => ({\n ...mergedStyles.input,\n ...(form.fieldErrors[field] ? mergedStyles.inputError : {}),\n });\n\n const isDisabled = !username || !password || form.loading;\n const buttonStyle = {\n ...mergedStyles.button,\n ...(form.loading ? mergedStyles.buttonLoading : {}),\n ...(isDisabled ? 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={form.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 form.clearFieldError('username');\n }}\n placeholder={mergedCopy.usernamePlaceholder}\n style={getInputStyle('username')}\n disabled={form.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 form.clearFieldError('password');\n }}\n placeholder={mergedCopy.passwordPlaceholder}\n style={{ ...getInputStyle('password'), ...mergedStyles.inputWithIcon }}\n disabled={form.loading}\n />\n <button\n type=\"button\"\n onClick={() => setShowPassword(!showPassword)}\n style={mergedStyles.passwordToggle}\n disabled={form.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={isDisabled} style={buttonStyle}>\n {form.loading ? mergedCopy.loadingText : mergedCopy.submitButton}\n </button>\n\n {form.error && <div style={mergedStyles.errorText}>{form.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 { useState } from 'react';\nimport { useAuth } from '../providers/AuthProvider';\nimport { useTenantOptional } from '../providers/TenantProvider';\nimport { useAuthForm } from '../hooks/useAuthForm';\nimport { AuthFormBaseStyles, buildFormStyles } from './authFormShared';\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 type SignupFormStyles = AuthFormBaseStyles;\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\ntype SignupField = 'name' | 'email' | 'phoneNumber' | 'password' | 'confirmPassword' | 'tenantName';\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\n const { signup, signupTenantAdmin } = useAuth();\n const tenant = useTenantOptional()?.tenant ?? null;\n\n const mergedCopy = { ...defaultCopy, ...copy };\n const mergedStyles = buildFormStyles('#10b981', styles);\n\n const isFormValid =\n !!name &&\n (!!email || !!phoneNumber) &&\n !!password &&\n !!confirmPassword &&\n (signupType === 'user' || !!tenantName);\n\n const form = useAuthForm<unknown, SignupField>({\n defaultErrorMessage: mergedCopy.errorMessage,\n validate: () => {\n const missing: SignupField[] = [];\n if (!name.trim()) missing.push('name');\n if (!email.trim() && !phoneNumber.trim()) {\n missing.push('email');\n missing.push('phoneNumber');\n }\n if (!password.trim()) missing.push('password');\n if (!confirmPassword.trim()) missing.push('confirmPassword');\n if (signupType === 'tenant' && !tenantName.trim()) missing.push('tenantName');\n\n missing.forEach(f => form.setFieldError(f, true));\n if (missing.length > 0) return false;\n\n if (password !== confirmPassword) {\n form.setError(mergedCopy.passwordMismatchError);\n form.setFieldError('confirmPassword', true);\n return false;\n }\n\n if (signupType === 'user' && !tenant?.id) {\n form.setError(mergedCopy.tenantNotFoundError);\n return false;\n }\n\n return true;\n },\n submit: async () => {\n if (signupType === 'tenant') {\n return signupTenantAdmin({\n email: email || undefined,\n phoneNumber: phoneNumber || undefined,\n name,\n password,\n tenantName,\n lastName: lastName || undefined,\n });\n }\n return 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,\n onError,\n });\n\n const getInputStyle = (field: SignupField) => ({\n ...mergedStyles.input,\n ...(form.fieldErrors[field] ? mergedStyles.inputError : {}),\n });\n\n const isDisabled = !isFormValid || form.loading;\n const buttonStyle = {\n ...mergedStyles.button,\n ...(form.loading ? mergedStyles.buttonLoading : {}),\n ...(isDisabled ? mergedStyles.buttonDisabled : {}),\n };\n\n const onEmailOrPhoneChange = () => {\n form.clearFieldError('email');\n form.clearFieldError('phoneNumber');\n };\n\n return (\n <div className={className} style={mergedStyles.container}>\n <h2 style={mergedStyles.title}>{mergedCopy.title}</h2>\n\n <form onSubmit={form.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 form.clearFieldError('name');\n }}\n placeholder={mergedCopy.namePlaceholder}\n style={getInputStyle('name')}\n disabled={form.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={form.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 onEmailOrPhoneChange();\n }}\n placeholder={mergedCopy.emailPlaceholder}\n style={getInputStyle('email')}\n disabled={form.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 onEmailOrPhoneChange();\n }}\n placeholder={mergedCopy.phoneNumberPlaceholder}\n style={getInputStyle('phoneNumber')}\n disabled={form.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 form.clearFieldError('password');\n }}\n placeholder={mergedCopy.passwordPlaceholder}\n style={getInputStyle('password')}\n disabled={form.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 form.clearFieldError('confirmPassword');\n if (form.error === mergedCopy.passwordMismatchError) {\n form.setError('');\n }\n }}\n placeholder={mergedCopy.confirmPasswordPlaceholder}\n style={getInputStyle('confirmPassword')}\n disabled={form.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 form.clearFieldError('tenantName');\n }}\n placeholder={mergedCopy.tenantNamePlaceholder}\n style={getInputStyle('tenantName')}\n disabled={form.loading}\n />\n </div>\n )}\n\n <button type=\"submit\" disabled={isDisabled} style={buttonStyle}>\n {form.loading ? mergedCopy.loadingText : mergedCopy.submitButton}\n </button>\n\n {form.error && <div style={mergedStyles.errorText}>{form.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 { useState, useEffect } from 'react';\nimport { useAuth } from '../providers/AuthProvider';\nimport { useTenantOptional } from '../providers/TenantProvider';\nimport { useAuthForm } from '../hooks/useAuthForm';\nimport { AuthFormBaseStyles, buildFormStyles } from './authFormShared';\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 type MagicLinkFormStyles = AuthFormBaseStyles;\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 verifyToken?: string;\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\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 [verifying, setVerifying] = useState(false);\n const [success, setSuccess] = useState('');\n const [showNameFields, setShowNameFields] = useState(false);\n\n const { sendMagicLink, verifyMagicLink } = useAuth();\n const tenant = useTenantOptional()?.tenant ?? null;\n\n const mergedCopy = { ...defaultCopy, ...copy };\n const mergedStyles = buildFormStyles('#3b82f6', styles);\n\n type Field = 'email' | 'name';\n const form = useAuthForm<unknown, Field>({\n defaultErrorMessage: mergedCopy.errorMessage,\n validate: () => {\n const missing: Field[] = [];\n if (!email.trim()) missing.push('email');\n if (showNameFields && !name.trim()) missing.push('name');\n missing.forEach(f => form.setFieldError(f, true));\n if (missing.length > 0) return false;\n if (!tenant?.id) {\n form.setError(mergedCopy.tenantNotFoundError);\n return false;\n }\n return true;\n },\n submit: async () => {\n setSuccess('');\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 return result;\n },\n onSuccess,\n onError,\n });\n\n useEffect(() => {\n if (!verifyToken) return;\n\n const run = async () => {\n if (!tenant || !email) {\n form.setError(mergedCopy.missingTenantOrEmailError);\n return;\n }\n setVerifying(true);\n form.setError('');\n try {\n const result = await verifyMagicLink({ token: verifyToken, email });\n onSuccess?.(result);\n } catch (err) {\n const message = err instanceof Error ? err.message : 'Failed to verify magic link';\n form.setError(message);\n onError?.(message);\n } finally {\n setVerifying(false);\n }\n };\n\n run();\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [verifyToken]);\n\n const getInputStyle = (field: Field) => ({\n ...mergedStyles.input,\n ...(form.fieldErrors[field] ? mergedStyles.inputError : {}),\n });\n\n const isDisabled = !email || form.loading || verifying;\n const buttonStyle = {\n ...mergedStyles.button,\n ...(form.loading || verifying ? mergedStyles.buttonLoading : {}),\n ...(isDisabled ? 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={form.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 form.clearFieldError('email');\n }}\n placeholder={mergedCopy.emailPlaceholder}\n style={getInputStyle('email')}\n disabled={form.loading || verifying}\n />\n </div>\n\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 form.clearFieldError('name');\n }}\n placeholder={mergedCopy.namePlaceholder}\n style={getInputStyle('name')}\n disabled={form.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={form.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={isDisabled} style={buttonStyle}>\n {form.loading ? mergedCopy.loadingText : mergedCopy.submitButton}\n </button>\n\n {form.error && <div style={mergedStyles.errorText}>{form.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 (dedup handled at service level)\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 { useState } from 'react';\nimport { useAuth } from '../providers/AuthProvider';\nimport { useTenantOptional } from '../providers/TenantProvider';\nimport { useAuthForm } from '../hooks/useAuthForm';\nimport { AuthFormBaseStyles, buildFormStyles } from './authFormShared';\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 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 type PasswordRecoveryFormStyles = AuthFormBaseStyles;\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\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 [success, setSuccess] = useState('');\n\n const { requestPasswordReset, confirmPasswordReset } = useAuth();\n const tenant = useTenantOptional()?.tenant ?? null;\n\n const mergedCopy = { ...defaultCopy, ...copy };\n const mergedStyles = buildFormStyles('#f59e0b', styles);\n\n type RequestField = 'email';\n const requestForm = useAuthForm<void, RequestField>({\n defaultErrorMessage: mergedCopy.errorMessage,\n validate: () => {\n if (!email.trim()) {\n requestForm.setFieldError('email', true);\n return false;\n }\n if (!tenant?.id) {\n requestForm.setError(mergedCopy.tenantNotFoundError);\n return false;\n }\n return true;\n },\n submit: async () => {\n setSuccess('');\n await requestPasswordReset({ email, tenantId: tenant!.id });\n setSuccess(mergedCopy.successMessage);\n },\n onSuccess: () => onSuccess?.(),\n onError,\n });\n\n type ResetField = 'token' | 'newPassword' | 'confirmPassword';\n const resetForm = useAuthForm<void, ResetField>({\n defaultErrorMessage: mergedCopy.errorMessage,\n validate: () => {\n const missing: ResetField[] = [];\n if (!token.trim()) missing.push('token');\n if (!newPassword.trim()) missing.push('newPassword');\n if (!confirmPassword.trim()) missing.push('confirmPassword');\n missing.forEach(f => resetForm.setFieldError(f, true));\n if (missing.length > 0) return false;\n\n if (newPassword !== confirmPassword) {\n resetForm.setError(mergedCopy.passwordMismatchError);\n resetForm.setFieldError('confirmPassword', true);\n return false;\n }\n return true;\n },\n submit: async () => {\n setSuccess('');\n await confirmPasswordReset({ token, newPassword });\n setSuccess(mergedCopy.resetSuccessMessage);\n },\n onSuccess: () => onSuccess?.(),\n onError,\n });\n\n if (mode === 'reset') {\n const getInputStyle = (field: ResetField) => ({\n ...mergedStyles.input,\n ...(resetForm.fieldErrors[field] ? mergedStyles.inputError : {}),\n });\n\n const isFormValid = !!token && !!newPassword && !!confirmPassword;\n const isDisabled = !isFormValid || resetForm.loading;\n const buttonStyle = {\n ...mergedStyles.button,\n ...(resetForm.loading ? mergedStyles.buttonLoading : {}),\n ...(isDisabled ? mergedStyles.buttonDisabled : {}),\n };\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={resetForm.handleSubmit} 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 resetForm.clearFieldError('token');\n }}\n placeholder={mergedCopy.tokenPlaceholder}\n style={getInputStyle('token')}\n disabled={resetForm.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 resetForm.clearFieldError('newPassword');\n }}\n placeholder={mergedCopy.newPasswordPlaceholder}\n style={getInputStyle('newPassword')}\n disabled={resetForm.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 resetForm.clearFieldError('confirmPassword');\n if (resetForm.error === mergedCopy.passwordMismatchError) {\n resetForm.setError('');\n }\n }}\n placeholder={mergedCopy.confirmPasswordPlaceholder}\n style={getInputStyle('confirmPassword')}\n disabled={resetForm.loading}\n />\n </div>\n\n <button type=\"submit\" disabled={isDisabled} style={buttonStyle}>\n {resetForm.loading ? mergedCopy.resetLoadingText : mergedCopy.resetSubmitButton}\n </button>\n\n {resetForm.error && <div style={mergedStyles.errorText}>{resetForm.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 getInputStyle = (field: RequestField) => ({\n ...mergedStyles.input,\n ...(requestForm.fieldErrors[field] ? mergedStyles.inputError : {}),\n });\n\n const isDisabled = !email || requestForm.loading;\n const buttonStyle = {\n ...mergedStyles.button,\n ...(requestForm.loading ? mergedStyles.buttonLoading : {}),\n ...(isDisabled ? mergedStyles.buttonDisabled : {}),\n };\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={requestForm.handleSubmit} 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 requestForm.clearFieldError('email');\n }}\n placeholder={mergedCopy.emailPlaceholder}\n style={getInputStyle('email')}\n disabled={requestForm.loading}\n />\n </div>\n\n <button type=\"submit\" disabled={isDisabled} style={buttonStyle}>\n {requestForm.loading ? mergedCopy.loadingText : mergedCopy.submitButton}\n </button>\n\n {requestForm.error && <div style={mergedStyles.errorText}>{requestForm.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 type {\n Permission,\n CreatePermissionRequest,\n ApiResponse,\n PaginationParams,\n} from '../types/api';\nimport { buildPaginationQuery } from '../utils/query';\n\nexport class PermissionApiService {\n constructor(private httpService: HttpService) {}\n\n async createPermission(request: CreatePermissionRequest): Promise<Permission> {\n const response = await this.httpService.post<ApiResponse<Permission>>('/permissions/', request);\n return response.data;\n }\n\n async getPermissions(\n params?: PaginationParams\n ): Promise<{ permissions: Permission[]; meta: any }> {\n const response = await this.httpService.get<ApiResponse<Permission[]>>(\n `/permissions/${buildPaginationQuery(params)}`\n );\n return { permissions: response.data, meta: response.meta };\n }\n\n async getPermissionById(id: string): Promise<Permission> {\n const response = await this.httpService.get<ApiResponse<Permission>>(`/permissions/${id}`);\n return response.data;\n }\n\n async updatePermission(\n id: string,\n request: Partial<CreatePermissionRequest>\n ): Promise<Permission> {\n const response = await this.httpService.put<ApiResponse<Permission>>(\n `/permissions/${id}`,\n request\n );\n return response.data;\n }\n\n async deletePermission(id: string): Promise<void> {\n await this.httpService.delete<void>(`/permissions/${id}`);\n }\n\n async getAppPermissions(\n appId: string,\n params?: PaginationParams\n ): Promise<{ permissions: Permission[]; meta: any }> {\n const response = await this.httpService.get<ApiResponse<Permission[]>>(\n `/permissions/apps/${appId}${buildPaginationQuery(params)}`,\n { skipAuth: true }\n );\n return { permissions: response.data, meta: response.meta };\n }\n}\n","import { HttpService } from './HttpService';\nimport type {\n SubscriptionPlan,\n CreateSubscriptionPlanRequest,\n ApiResponse,\n PaginationParams,\n} from '../types/api';\nimport { buildPaginationQuery } from '../utils/query';\n\nexport class SubscriptionPlanApiService {\n constructor(private httpService: HttpService) {}\n\n async createSubscriptionPlan(request: CreateSubscriptionPlanRequest): Promise<SubscriptionPlan> {\n const response = await this.httpService.post<ApiResponse<SubscriptionPlan>>(\n '/subscription-plans/',\n request\n );\n return response.data;\n }\n\n async getSubscriptionPlans(\n params?: PaginationParams & { appId?: string }\n ): Promise<{ plans: SubscriptionPlan[]; meta: any }> {\n const response = await this.httpService.get<ApiResponse<SubscriptionPlan[]>>(\n `/subscription-plans/${buildPaginationQuery(params)}`\n );\n return { plans: response.data, meta: response.meta };\n }\n\n async getSubscriptionPlanById(id: string): Promise<SubscriptionPlan> {\n const response = await this.httpService.get<ApiResponse<SubscriptionPlan>>(\n `/subscription-plans/${id}`\n );\n return response.data;\n }\n\n async updateSubscriptionPlan(\n id: string,\n request: Partial<CreateSubscriptionPlanRequest>\n ): Promise<SubscriptionPlan> {\n const response = await this.httpService.put<ApiResponse<SubscriptionPlan>>(\n `/subscription-plans/${id}`,\n request\n );\n return response.data;\n }\n\n async deleteSubscriptionPlan(id: string): Promise<void> {\n await this.httpService.delete<void>(`/subscription-plans/${id}`);\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","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","buildPaginationQuery","params","qp","key","value","str","AppApiService","httpService","request","id","appId","planId","schema","defaultSettings","AppContext","createContext","DEFAULT_CACHE_TTL","AppProvider","config","children","cacheEnabled","_a","cacheTtl","_b","cacheStorageKey","_c","appInfo","setAppInfo","useState","cached","parsed","isAppLoading","setIsAppLoading","appError","setAppError","appInfoRef","useRef","loadApp","useCallback","bypassCache","appData","cacheData","err","backgroundRefresh","contextValue","useMemo","useEffect","useApp","context","useContext","useAppOptional","useApi","SessionExpiredError","reason","message","defaultMessages","TokenRefreshTimeoutError","timeoutMs","TokenRefreshError","attempts","lastError","ConfigurationError","field","received","describeValue","v","json","decodeSegment","segment","decodeJwt","token","parts","extractJwtExpiry","decoded","exp","extractJwtClaim","claim","DANGEROUS_SCHEMES","SAFE_SCHEMES","validateBaseUrl","validateNumber","min","max","validateBoolean","validateTokenShape","validateExpiresIn","validateExpiresAt","_SessionManager","existing","instance","storageKey","stored","tokens","expiresAt","tokenData","currentData","refreshToken","expiresIn","tokenType","resolvedExpiresAt","delay","gen","resolve","reject","idx","e","newTokens","newAccessToken","queue","entry","attempt","backoff","freshTokens","currentRefreshToken","deviceId","refreshBody","networkError","errorMessage","body","refreshResponse","user","expiredError","payload","ms","SessionManager","AuthApiService","pending","promise","RoleApiService","roleId","userId","UserApiService","TenantApiService","slug","detectSubdomainTenant","hostname","baseDomain","baseDomainLower","currentHost","subdomain","detectSelectorTenant","search","selectorParam","localStorage","urlTenant","detectTenantSlug","location","tenantMode","fixedTenantSlug","buildTenantHostname","targetTenantSlug","currentHostname","TenantContext","TenantProvider","detectTenant","tenantSlug","setTenantSlug","cacheConfig","tenant","setTenant","isTenantLoading","setIsTenantLoading","tenantError","setTenantError","settings","setSettings","isSettingsLoading","setIsSettingsLoading","settingsError","setSettingsError","detected","settingsSchema","loadTenant","tenantInfo","loadSettings","tenantSettings","refreshSettings","validateSettings","settingsToValidate","errors","fieldSchema","expectedType","actualType","switchTenant","mode","redirectPath","newHostname","targetPath","urlParams","newUrl","useTenant","useTenantOptional","useTenantSettings","useSettings","useTenantInfo","retryTenant","USER_TENANTS_STORAGE_KEY","readUserTenants","writeUserTenants","tenants","clearUserTenants","AuthStateContext","AuthActionsContext","AuthProvider","appContext","tenantContext","availableRoles","setAvailableRoles","rolesLoading","setRolesLoading","currentUser","setCurrentUser","isUserLoading","setIsUserLoading","userError","setUserError","userTenants","setUserTenants","initRef","isRestoringSession","setIsRestoringSession","isAuthReady","authenticatedHttpService","service","authApiService","userApiService","roleApiService","userRole","role","userPermissions","isAuthenticated","hasTenantContext","actionsImplRef","loadUserData","forceRefresh","userData","login","username","password","targetSlug","resolvedTenantId","loginResponse","shouldSwitch","hasTenant","autoSwitch","singleTenant","signup","email","phoneNumber","name","lastName","tenantId","signupTenantAdmin","tenantName","changePassword","requestPasswordReset","confirmPasswordReset","sendMagicLink","frontendUrl","verifyMagicLink","verifyResponse","logout","setTokens","hasValidSession","clearSession","refreshRoles","roles","switchToTenant","targetTenant","t","refreshUserTenants","actions","stateValue","hasPermission","permission","permissions","p","cancelled","jsx","useAuthState","state","useAuthActions","useAuth","useAuthOptional","FeatureFlagApiService","query","flagKey","FeatureFlagContext","FeatureFlagProvider","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","DEFAULT_ROUTING_CONTEXT","RoutingProvider","zoneRoots","presets","useRouting","useRoutingOptional","DefaultFallback","jsxs","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","perms","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","useAuthForm","submit","defaultErrorMessage","validate","onSuccess","onError","fieldErrors","setFieldErrors","setFieldError","prev","clearFieldError","next","resetErrors","handleSubmit","result","baseFormStyles","buildFormStyles","buttonBackgroundColor","userStyles","EyeIcon","React","EyeOffIcon","defaultIcons","defaultCopy","LoginForm","copy","styles","icons","onForgotPassword","onSignupClick","onMagicLinkClick","showForgotPassword","showSignupLink","showMagicLinkOption","className","setUsername","setPassword","showPassword","setShowPassword","mergedCopy","mergedStyles","mergedIcons","form","missing","getInputStyle","isDisabled","buttonStyle","SignupForm","signupType","onLoginClick","showLoginLink","setName","setLastName","setEmail","setPhoneNumber","confirmPassword","setConfirmPassword","setTenantName","isFormValid","onEmailOrPhoneChange","MagicLinkForm","showTraditionalLinks","verifyToken","verifying","setVerifying","success","setSuccess","showNameFields","setShowNameFields","finalFrontendUrl","defaultStyles","LoadingIcon","SuccessIcon","ErrorIcon","MagicLinkVerify","onRetry","onBackToLogin","propToken","propEmail","propAppId","propTenantSlug","autoRedirectDelay","setState","getUrlParams","handleVerification","handleRetry","handleBackToLogin","renderContent","base","PasswordRecoveryForm","initialToken","onModeChange","setToken","newPassword","setNewPassword","requestForm","resetForm","DefaultLoadingFallback","DefaultErrorFallback","retry","AppLoader","errorFallback","requireTenant","retryApp","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","DEFAULT_RETURN_TO_PARAM","RETURN_TO_SESSION_KEY","RETURN_TO_LOCAL_KEY","useZoneNavigation","navigate","useNavigate","searchParams","setSearchParams","useSearchParams","mergedZoneRoots","returnToUrl","clearReturnTo","newParams","setReturnTo","navigateToZone","zone","path"],"mappings":"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"}
|