@frank-auth/react
Version:
Flexible and customizable React UI components for Frank Authentication
1 lines • 29.3 kB
Source Map (JSON)
{"version":3,"file":"phone-verification.cjs","sources":["../../../../../../src/components/auth/verification/phone-verification/phone-verification.tsx"],"sourcesContent":["/**\n * @frank-auth/react - Phone Verification Component\n *\n * Handles phone verification flow with SMS code sending, validation, and resending.\n * Integrates with Frank Auth API for SMS-based identity verification.\n */\n\nimport {\n\tButton,\n\tCard,\n\tCardBody,\n\tChip,\n\tModal,\n\tModalBody,\n\tModalHeader,\n} from \"@/components/ui\";\nimport {\n\tArrowPathIcon,\n\tCheckCircleIcon,\n\tDevicePhoneMobileIcon,\n\tXCircleIcon,\n} from \"@heroicons/react/24/outline\";\nimport type React from \"react\";\nimport { useCallback, useEffect, useState } from \"react\";\n\nimport { useAuth } from \"../../../../hooks/use-auth\";\nimport { useConfig } from \"../../../../hooks/use-config\";\nimport {\n\tVerificationError,\n\tVerificationInput,\n\twithErrorBoundary,\n} from \"../../common\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface PhoneVerificationProps {\n\tphoneNumber?: string;\n\tuserId?: string;\n\torganizationId?: string;\n\tautoSubmit?: boolean;\n\tcodeLength?: number;\n\tresendDelay?: number;\n\tmaxResendAttempts?: number;\n\texpirationTime?: number;\n\tonVerificationSuccess?: (result: {\n\t\tphoneNumber: string;\n\t\tverified: boolean;\n\t}) => void;\n\tonVerificationError?: (error: Error) => void;\n\tonCodeSent?: () => void;\n\tonCodeResent?: (attempt: number) => void;\n\tclassName?: string;\n\tstyle?: React.CSSProperties;\n\tdisabled?: boolean;\n}\n\nexport interface PhoneVerificationFormProps extends PhoneVerificationProps {\n\tshowHeader?: boolean;\n\tshowInstructions?: boolean;\n}\n\nexport interface PhoneVerificationModalProps extends PhoneVerificationProps {\n\tisOpen: boolean;\n\tonClose: () => void;\n\tisDismissable?: boolean;\n}\n\nexport interface PhoneVerificationCardProps extends PhoneVerificationProps {\n\tvariant?: \"flat\" | \"bordered\" | \"shadow\";\n\tradius?: \"none\" | \"sm\" | \"md\" | \"lg\";\n}\n\nexport interface PhoneVerificationStatusProps {\n\tstatus: \"sent\" | \"verified\" | \"error\" | \"expired\";\n\tphoneNumber: string;\n\tonRetry?: () => void;\n\tclassName?: string;\n}\n\nexport interface ResendSMSButtonProps {\n\tonResend: () => void;\n\tdisabled?: boolean;\n\tremainingTime?: number;\n\tattempt?: number;\n\tmaxAttempts?: number;\n\tclassName?: string;\n}\n\n// ============================================================================\n// Hook for Phone Verification\n// ============================================================================\n\ninterface UsePhoneVerificationProps {\n\tphoneNumber?: string;\n\tuserId?: string;\n\torganizationId?: string;\n\tautoSubmit?: boolean;\n\tcodeLength?: number;\n\tresendDelay?: number;\n\tmaxResendAttempts?: number;\n\texpirationTime?: number;\n\tonVerificationSuccess?: (result: {\n\t\tphoneNumber: string;\n\t\tverified: boolean;\n\t}) => void;\n\tonVerificationError?: (error: Error) => void;\n\tonCodeSent?: () => void;\n\tonCodeResent?: (attempt: number) => void;\n}\n\nfunction usePhoneVerification({\n\tphoneNumber,\n\tuserId,\n\torganizationId,\n\tautoSubmit = true,\n\tcodeLength = 6,\n\tresendDelay = 30,\n\tmaxResendAttempts = 3,\n\texpirationTime = 300, // 5 minutes\n\tonVerificationSuccess,\n\tonVerificationError,\n\tonCodeSent,\n\tonCodeResent,\n}: UsePhoneVerificationProps) {\n\tconst { client } = useAuth();\n\tconst [code, setCode] = useState(\"\");\n\tconst [status, setStatus] = useState<\n\t\t\"idle\" | \"sending\" | \"sent\" | \"verifying\" | \"verified\" | \"error\" | \"expired\"\n\t>(\"idle\");\n\tconst [error, setError] = useState<string | null>(null);\n\tconst [resendAttempts, setResendAttempts] = useState(0);\n\tconst [timeRemaining, setTimeRemaining] = useState(0);\n\tconst [expiresAt, setExpiresAt] = useState<Date | null>(null);\n\n\t// Timer effect for resend delay\n\tuseEffect(() => {\n\t\tif (timeRemaining > 0) {\n\t\t\tconst timer = setTimeout(\n\t\t\t\t() => setTimeRemaining((prev) => prev - 1),\n\t\t\t\t1000,\n\t\t\t);\n\t\t\treturn () => clearTimeout(timer);\n\t\t}\n\t}, [timeRemaining]);\n\n\t// Timer effect for expiration\n\tuseEffect(() => {\n\t\tif (expiresAt) {\n\t\t\tconst checkExpiration = () => {\n\t\t\t\tif (new Date() > expiresAt) {\n\t\t\t\t\tsetStatus(\"expired\");\n\t\t\t\t\tsetError(\"Verification code has expired\");\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tconst timer = setInterval(checkExpiration, 1000);\n\t\t\treturn () => clearInterval(timer);\n\t\t}\n\t}, [expiresAt]);\n\n\t// Auto-submit when code is complete\n\tuseEffect(() => {\n\t\tif (autoSubmit && code.length === codeLength && status === \"sent\") {\n\t\t\thandleVerify();\n\t\t}\n\t}, [code, codeLength, autoSubmit, status]);\n\n\tconst sendCode = useCallback(async () => {\n\t\tif (!phoneNumber || !userId || !organizationId) {\n\t\t\tsetError(\"Missing required parameters for phone verification\");\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tsetStatus(\"sending\");\n\t\t\tsetError(null);\n\n\t\t\tconst response = await client.mfa.sendSMSCode({\n\t\t\t\torgId: organizationId,\n\t\t\t\tuserId: userId,\n\t\t\t});\n\n\t\t\tif (response.success) {\n\t\t\t\tsetStatus(\"sent\");\n\t\t\t\tsetTimeRemaining(resendDelay);\n\t\t\t\tsetExpiresAt(new Date(Date.now() + expirationTime * 1000));\n\t\t\t\tonCodeSent?.();\n\t\t\t} else {\n\t\t\t\tthrow new Error(\n\t\t\t\t\tresponse.message || \"Failed to send SMS verification code\",\n\t\t\t\t);\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tconst error =\n\t\t\t\terr instanceof Error\n\t\t\t\t\t? err\n\t\t\t\t\t: new Error(\"Failed to send SMS verification code\");\n\t\t\tsetStatus(\"error\");\n\t\t\tsetError(error.message);\n\t\t\tonVerificationError?.(error);\n\t\t}\n\t}, [\n\t\tphoneNumber,\n\t\tuserId,\n\t\torganizationId,\n\t\tclient,\n\t\tresendDelay,\n\t\texpirationTime,\n\t\tonCodeSent,\n\t\tonVerificationError,\n\t]);\n\n\tconst resendCode = useCallback(async () => {\n\t\tif (resendAttempts >= maxResendAttempts) {\n\t\t\tsetError(\"Maximum resend attempts reached\");\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tsetResendAttempts((prev) => prev + 1);\n\t\t\tawait sendCode();\n\t\t\tonCodeResent?.(resendAttempts + 1);\n\t\t} catch (err) {\n\t\t\tconst error =\n\t\t\t\terr instanceof Error ? err : new Error(\"Failed to resend SMS code\");\n\t\t\tonVerificationError?.(error);\n\t\t}\n\t}, [\n\t\tresendAttempts,\n\t\tmaxResendAttempts,\n\t\tsendCode,\n\t\tonCodeResent,\n\t\tonVerificationError,\n\t]);\n\n\tconst handleVerify = useCallback(async () => {\n\t\tif (!code || code.length !== codeLength) {\n\t\t\tsetError(\"Please enter a valid verification code\");\n\t\t\treturn;\n\t\t}\n\n\t\tif (!phoneNumber) {\n\t\t\tsetError(\"Phone number is required for verification\");\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tsetStatus(\"verifying\");\n\t\t\tsetError(null);\n\n\t\t\t// For phone verification, we use the public API endpoint\n\t\t\tconst response = await client.authentication.verifyPhone({\n\t\t\t\tverificationRequest: {\n\t\t\t\t\tphoneNumber: phoneNumber,\n\t\t\t\t\tcode: code,\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tif (response.verified) {\n\t\t\t\tsetStatus(\"verified\");\n\t\t\t\tonVerificationSuccess?.({\n\t\t\t\t\tphoneNumber: phoneNumber || \"\",\n\t\t\t\t\tverified: true,\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tthrow new Error(\"Invalid verification code\");\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tconst error =\n\t\t\t\terr instanceof Error ? err : new Error(\"Phone verification failed\");\n\t\t\tsetStatus(\"error\");\n\t\t\tsetError(error.message);\n\t\t\tonVerificationError?.(error);\n\t\t}\n\t}, [\n\t\tcode,\n\t\tcodeLength,\n\t\tphoneNumber,\n\t\tclient,\n\t\tonVerificationSuccess,\n\t\tonVerificationError,\n\t]);\n\n\tconst reset = useCallback(() => {\n\t\tsetCode(\"\");\n\t\tsetStatus(\"idle\");\n\t\tsetError(null);\n\t\tsetResendAttempts(0);\n\t\tsetTimeRemaining(0);\n\t\tsetExpiresAt(null);\n\t}, []);\n\n\treturn {\n\t\tcode,\n\t\tsetCode,\n\t\tstatus,\n\t\terror,\n\t\tresendAttempts,\n\t\ttimeRemaining,\n\t\texpiresAt,\n\t\tcanResend: timeRemaining === 0 && resendAttempts < maxResendAttempts,\n\t\tsendCode,\n\t\tresendCode,\n\t\thandleVerify,\n\t\treset,\n\t};\n}\n\n// ============================================================================\n// Utility Functions\n// ============================================================================\n\nfunction formatPhoneNumber(phoneNumber: string): string {\n\t// Basic phone number formatting\n\tconst cleaned = phoneNumber.replace(/\\D/g, \"\");\n\tif (cleaned.length === 10) {\n\t\treturn `(${cleaned.slice(0, 3)}) ${cleaned.slice(3, 6)}-${cleaned.slice(6)}`;\n\t}\n\tif (cleaned.length === 11 && cleaned[0] === \"1\") {\n\t\treturn `+1 (${cleaned.slice(1, 4)}) ${cleaned.slice(4, 7)}-${cleaned.slice(7)}`;\n\t}\n\treturn phoneNumber;\n}\n\nfunction maskPhoneNumber(phoneNumber: string): string {\n\tconst formatted = formatPhoneNumber(phoneNumber);\n\t// Mask middle digits: +1 (555) 123-4567 → +1 (555) ***-4567\n\treturn formatted.replace(/(\\d{3})-(\\d{4})/, \"***-$2\");\n}\n\n// ============================================================================\n// Main Phone Verification Component\n// ============================================================================\n\nexport const PhoneVerification = withErrorBoundary(function PhoneVerification({\n\tphoneNumber,\n\tuserId,\n\torganizationId,\n\tautoSubmit = true,\n\tcodeLength = 6,\n\tresendDelay = 30,\n\tmaxResendAttempts = 3,\n\texpirationTime = 300,\n\tonVerificationSuccess,\n\tonVerificationError,\n\tonCodeSent,\n\tonCodeResent,\n\tclassName,\n\tstyle,\n\tdisabled = false,\n}: PhoneVerificationProps) {\n\tconst { config } = useConfig();\n\tconst {\n\t\tcode,\n\t\tsetCode,\n\t\tstatus,\n\t\terror,\n\t\tresendAttempts,\n\t\ttimeRemaining,\n\t\tcanResend,\n\t\tsendCode,\n\t\tresendCode,\n\t\thandleVerify,\n\t} = usePhoneVerification({\n\t\tphoneNumber,\n\t\tuserId,\n\t\torganizationId,\n\t\tautoSubmit,\n\t\tcodeLength,\n\t\tresendDelay,\n\t\tmaxResendAttempts,\n\t\texpirationTime,\n\t\tonVerificationSuccess,\n\t\tonVerificationError,\n\t\tonCodeSent,\n\t\tonCodeResent,\n\t});\n\n\tconst handleCodeChange = (value: string) => {\n\t\tif (value.length <= codeLength) {\n\t\t\tsetCode(value);\n\t\t}\n\t};\n\n\tconst handleSubmit = (e: React.FormEvent) => {\n\t\te.preventDefault();\n\t\thandleVerify();\n\t};\n\n\tconst displayPhoneNumber = phoneNumber ? maskPhoneNumber(phoneNumber) : \"\";\n\n\tconst renderContent = () => {\n\t\tif (status === \"verified\") {\n\t\t\treturn (\n\t\t\t\t<div className=\"text-center py-8\">\n\t\t\t\t\t<CheckCircleIcon className=\"h-16 w-16 text-success mx-auto mb-4\" />\n\t\t\t\t\t<h3 className=\"text-lg font-semibold mb-2\">Phone Verified!</h3>\n\t\t\t\t\t<p className=\"text-default-500\">\n\t\t\t\t\t\tYour phone number has been successfully verified.\n\t\t\t\t\t</p>\n\t\t\t\t</div>\n\t\t\t);\n\t\t}\n\n\t\tif (status === \"expired\") {\n\t\t\treturn (\n\t\t\t\t<div className=\"text-center py-8\">\n\t\t\t\t\t<XCircleIcon className=\"h-16 w-16 text-warning mx-auto mb-4\" />\n\t\t\t\t\t<h3 className=\"text-lg font-semibold mb-2\">Code Expired</h3>\n\t\t\t\t\t<p className=\"text-default-500 mb-4\">\n\t\t\t\t\t\tThe SMS verification code has expired. Please request a new one.\n\t\t\t\t\t</p>\n\t\t\t\t\t<Button\n\t\t\t\t\t\tcolor=\"primary\"\n\t\t\t\t\t\tonClick={sendCode}\n\t\t\t\t\t\tdisabled={disabled}\n\t\t\t\t\t\tstartContent={<ArrowPathIcon className=\"h-4 w-4\" />}\n\t\t\t\t\t>\n\t\t\t\t\t\tSend New Code\n\t\t\t\t\t</Button>\n\t\t\t\t</div>\n\t\t\t);\n\t\t}\n\n\t\treturn (\n\t\t\t<form onSubmit={handleSubmit} className=\"space-y-4\">\n\t\t\t\t<div className=\"text-center\">\n\t\t\t\t\t<DevicePhoneMobileIcon className=\"h-12 w-12 text-primary mx-auto mb-4\" />\n\t\t\t\t\t<h3 className=\"text-lg font-semibold mb-2\">Verify Your Phone</h3>\n\t\t\t\t\t<p className=\"text-default-500 mb-4\">\n\t\t\t\t\t\tWe've sent a verification code to {displayPhoneNumber}\n\t\t\t\t\t</p>\n\t\t\t\t</div>\n\n\t\t\t\t{error && <VerificationError error={error} />}\n\n\t\t\t\t<div className=\"space-y-4\">\n\t\t\t\t\t<VerificationInput\n\t\t\t\t\t\tvalue={code}\n\t\t\t\t\t\tonChange={handleCodeChange}\n\t\t\t\t\t\tlength={codeLength}\n\t\t\t\t\t\tdisabled={disabled || status === \"verifying\"}\n\t\t\t\t\t\tplaceholder=\"Enter SMS code\"\n\t\t\t\t\t\ttype=\"tel\"\n\t\t\t\t\t/>\n\n\t\t\t\t\t<Button\n\t\t\t\t\t\ttype=\"submit\"\n\t\t\t\t\t\tcolor=\"primary\"\n\t\t\t\t\t\tclassName=\"w-full\"\n\t\t\t\t\t\tisLoading={status === \"verifying\"}\n\t\t\t\t\t\tdisabled={\n\t\t\t\t\t\t\tdisabled || code.length !== codeLength || status === \"verifying\"\n\t\t\t\t\t\t}\n\t\t\t\t\t>\n\t\t\t\t\t\t{status === \"verifying\" ? \"Verifying...\" : \"Verify Phone\"}\n\t\t\t\t\t</Button>\n\n\t\t\t\t\t<div className=\"flex items-center justify-between\">\n\t\t\t\t\t\t<span className=\"text-sm text-default-500\">\n\t\t\t\t\t\t\tDidn't receive the code?\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<ResendSMSButton\n\t\t\t\t\t\t\tonResend={resendCode}\n\t\t\t\t\t\t\tdisabled={disabled || !canResend}\n\t\t\t\t\t\t\tremainingTime={timeRemaining}\n\t\t\t\t\t\t\tattempt={resendAttempts}\n\t\t\t\t\t\t\tmaxAttempts={maxResendAttempts}\n\t\t\t\t\t\t/>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\n\t\t\t\t{status === \"idle\" && (\n\t\t\t\t\t<Button\n\t\t\t\t\t\tcolor=\"primary\"\n\t\t\t\t\t\tvariant=\"flat\"\n\t\t\t\t\t\tclassName=\"w-full\"\n\t\t\t\t\t\tonClick={sendCode}\n\t\t\t\t\t\tdisabled={disabled}\n\t\t\t\t\t\tisLoading={status === \"sending\"}\n\t\t\t\t\t>\n\t\t\t\t\t\t{status === \"sending\" ? \"Sending...\" : \"Send SMS Code\"}\n\t\t\t\t\t</Button>\n\t\t\t\t)}\n\t\t\t</form>\n\t\t);\n\t};\n\n\treturn (\n\t\t<div className={className} style={style}>\n\t\t\t{renderContent()}\n\t\t</div>\n\t);\n});\n\n// ============================================================================\n// Phone Verification Form Component\n// ============================================================================\n\nexport const PhoneVerificationForm = withErrorBoundary(\n\tfunction PhoneVerificationForm({\n\t\tshowHeader = true,\n\t\tshowInstructions = true,\n\t\t...props\n\t}: PhoneVerificationFormProps) {\n\t\treturn (\n\t\t\t<div className=\"space-y-6\">\n\t\t\t\t{showHeader && (\n\t\t\t\t\t<div className=\"text-center\">\n\t\t\t\t\t\t<h2 className=\"text-2xl font-bold\">Phone Verification</h2>\n\t\t\t\t\t\t{showInstructions && (\n\t\t\t\t\t\t\t<p className=\"text-default-500 mt-2\">\n\t\t\t\t\t\t\t\tPlease verify your phone number to continue\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\t\t\t\t<PhoneVerification {...props} />\n\t\t\t</div>\n\t\t);\n\t},\n);\n\n// ============================================================================\n// Phone Verification Modal Component\n// ============================================================================\n\nexport const PhoneVerificationModal = withErrorBoundary(\n\tfunction PhoneVerificationModal({\n\t\tisOpen,\n\t\tonClose,\n\t\tisDismissable = true,\n\t\t...props\n\t}: PhoneVerificationModalProps) {\n\t\treturn (\n\t\t\t<Modal\n\t\t\t\tisOpen={isOpen}\n\t\t\t\tonClose={onClose}\n\t\t\t\tisDismissable={isDismissable}\n\t\t\t\tsize=\"md\"\n\t\t\t\tclassNames={{\n\t\t\t\t\tbackdrop:\n\t\t\t\t\t\t\"bg-gradient-to-t from-zinc-900 to-zinc-900/10 backdrop-opacity-20\",\n\t\t\t\t}}\n\t\t\t>\n\t\t\t\t<ModalHeader className=\"flex flex-col gap-1\">\n\t\t\t\t\tPhone Verification\n\t\t\t\t</ModalHeader>\n\t\t\t\t<ModalBody>\n\t\t\t\t\t<PhoneVerification {...props} />\n\t\t\t\t</ModalBody>\n\t\t\t</Modal>\n\t\t);\n\t},\n);\n\n// ============================================================================\n// Phone Verification Card Component\n// ============================================================================\n\nexport const PhoneVerificationCard = withErrorBoundary(\n\tfunction PhoneVerificationCard({\n\t\tvariant = \"shadow\",\n\t\tradius = \"lg\",\n\t\t...props\n\t}: PhoneVerificationCardProps) {\n\t\treturn (\n\t\t\t<Card\n\t\t\t\tclassName={`max-w-md mx-auto ${props.className || \"\"}`}\n\t\t\t\tvariant={variant}\n\t\t\t\tradius={radius}\n\t\t\t>\n\t\t\t\t<CardBody className=\"p-6\">\n\t\t\t\t\t<PhoneVerification {...props} />\n\t\t\t\t</CardBody>\n\t\t\t</Card>\n\t\t);\n\t},\n);\n\n// ============================================================================\n// Phone Verification Status Component\n// ============================================================================\n\nexport const PhoneVerificationStatus = withErrorBoundary(\n\tfunction PhoneVerificationStatus({\n\t\tstatus,\n\t\tphoneNumber,\n\t\tonRetry,\n\t\tclassName,\n\t}: PhoneVerificationStatusProps) {\n\t\tconst getStatusConfig = () => {\n\t\t\tconst maskedPhone = maskPhoneNumber(phoneNumber);\n\n\t\t\tswitch (status) {\n\t\t\t\tcase \"sent\":\n\t\t\t\t\treturn {\n\t\t\t\t\t\tcolor: \"primary\" as const,\n\t\t\t\t\t\ticon: <DevicePhoneMobileIcon className=\"h-4 w-4\" />,\n\t\t\t\t\t\tmessage: `SMS code sent to ${maskedPhone}`,\n\t\t\t\t\t};\n\t\t\t\tcase \"verified\":\n\t\t\t\t\treturn {\n\t\t\t\t\t\tcolor: \"success\" as const,\n\t\t\t\t\t\ticon: <CheckCircleIcon className=\"h-4 w-4\" />,\n\t\t\t\t\t\tmessage: `Phone ${maskedPhone} verified successfully`,\n\t\t\t\t\t};\n\t\t\t\tcase \"error\":\n\t\t\t\t\treturn {\n\t\t\t\t\t\tcolor: \"danger\" as const,\n\t\t\t\t\t\ticon: <XCircleIcon className=\"h-4 w-4\" />,\n\t\t\t\t\t\tmessage: \"SMS verification failed\",\n\t\t\t\t\t};\n\t\t\t\tcase \"expired\":\n\t\t\t\t\treturn {\n\t\t\t\t\t\tcolor: \"warning\" as const,\n\t\t\t\t\t\ticon: <XCircleIcon className=\"h-4 w-4\" />,\n\t\t\t\t\t\tmessage: \"SMS code expired\",\n\t\t\t\t\t};\n\t\t\t\tdefault:\n\t\t\t\t\treturn {\n\t\t\t\t\t\tcolor: \"default\" as const,\n\t\t\t\t\t\ticon: <DevicePhoneMobileIcon className=\"h-4 w-4\" />,\n\t\t\t\t\t\tmessage: \"Phone verification\",\n\t\t\t\t\t};\n\t\t\t}\n\t\t};\n\n\t\tconst config = getStatusConfig();\n\n\t\treturn (\n\t\t\t<div\n\t\t\t\tclassName={`flex items-center justify-between p-3 rounded-lg ${className || \"\"}`}\n\t\t\t>\n\t\t\t\t<div className=\"flex items-center gap-3\">\n\t\t\t\t\t<Chip color={config.color} variant=\"flat\" startContent={config.icon}>\n\t\t\t\t\t\t{config.message}\n\t\t\t\t\t</Chip>\n\t\t\t\t</div>\n\t\t\t\t{(status === \"error\" || status === \"expired\") && onRetry && (\n\t\t\t\t\t<Button\n\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\tcolor=\"primary\"\n\t\t\t\t\t\tvariant=\"flat\"\n\t\t\t\t\t\tonClick={onRetry}\n\t\t\t\t\t\tstartContent={<ArrowPathIcon className=\"h-3 w-3\" />}\n\t\t\t\t\t>\n\t\t\t\t\t\tRetry\n\t\t\t\t\t</Button>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t);\n\t},\n);\n\n// ============================================================================\n// Resend SMS Button Component\n// ============================================================================\n\nexport const ResendSMSButton = withErrorBoundary(function ResendSMSButton({\n\tonResend,\n\tdisabled = false,\n\tremainingTime = 0,\n\tattempt = 0,\n\tmaxAttempts = 3,\n\tclassName,\n}: ResendSMSButtonProps) {\n\tconst canResend = remainingTime === 0 && attempt < maxAttempts;\n\tconst attemptsLeft = maxAttempts - attempt;\n\n\treturn (\n\t\t<Button\n\t\t\tsize=\"sm\"\n\t\t\tcolor=\"primary\"\n\t\t\tvariant=\"light\"\n\t\t\tonClick={onResend}\n\t\t\tdisabled={disabled || !canResend}\n\t\t\tclassName={className}\n\t\t\tstartContent={\n\t\t\t\t!canResend ? undefined : <ArrowPathIcon className=\"h-3 w-3\" />\n\t\t\t}\n\t\t>\n\t\t\t{remainingTime > 0\n\t\t\t\t? `Resend in ${remainingTime}s`\n\t\t\t\t: attempt >= maxAttempts\n\t\t\t\t\t? \"Max attempts reached\"\n\t\t\t\t\t: `Resend SMS${attempt > 0 ? ` (${attemptsLeft} left)` : \"\"}`}\n\t\t</Button>\n\t);\n});\n"],"names":["usePhoneVerification","phoneNumber","userId","organizationId","autoSubmit","codeLength","resendDelay","maxResendAttempts","expirationTime","onVerificationSuccess","onVerificationError","onCodeSent","onCodeResent","client","useAuth","code","setCode","useState","status","setStatus","error","setError","resendAttempts","setResendAttempts","timeRemaining","setTimeRemaining","expiresAt","setExpiresAt","useEffect","timer","prev","handleVerify","sendCode","useCallback","response","err","resendCode","reset","formatPhoneNumber","cleaned","maskPhoneNumber","PhoneVerification","withErrorBoundary","className","style","disabled","config","useConfig","canResend","handleCodeChange","value","handleSubmit","e","displayPhoneNumber","renderContent","jsxs","jsx","CheckCircleIcon","XCircleIcon","Button","ArrowPathIcon","DevicePhoneMobileIcon","VerificationError","VerificationInput","ResendSMSButton","PhoneVerificationForm","showHeader","showInstructions","props","PhoneVerificationModal","isOpen","onClose","isDismissable","Modal","ModalHeader","ModalBody","PhoneVerificationCard","variant","radius","Card","CardBody","PhoneVerificationStatus","onRetry","maskedPhone","Chip","onResend","remainingTime","attempt","maxAttempts","attemptsLeft"],"mappings":"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"}