UNPKG

payloadcms_otp_plugin

Version:

A comprehensive One-Time Password (OTP) authentication plugin for Payload CMS that enables secure passwordless authentication via SMS and email

118 lines (117 loc) 4.86 kB
'use client'; import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; import React, { useState, useEffect } from 'react'; import './otp-view.scss'; import { Button, useTranslation } from '@payloadcms/ui'; import OTPInput from './otp-input.js'; import TimerCountdown from './timer-count-down.js'; import useOtpHook from '../../hook/useOtpHook.js'; import { getOtpConfig } from '../../actions/index.js'; function OtpView({ initialTimer, className = '', otpLength: propOtpLength, expiredTime: propExpiredTime }) { const { resendNewOtp, submit, currentOtp, isTimedOut, resetKey, setCurrentOtp, setIsTimedOut } = useOtpHook(); const { t } = useTranslation(); const [otpLength, setOtpLength] = useState(propOtpLength || 6); const [expiredTime, setExpiredTime] = useState(propExpiredTime || initialTimer || 120); const [isConfigLoaded, setIsConfigLoaded] = useState(// Only mark as loaded if both critical props are provided !!(propOtpLength && (propExpiredTime || initialTimer))); // Fetch OTP configuration only if not provided as props useEffect(()=>{ if (!propOtpLength || !propExpiredTime) { const fetchOtpConfig = async ()=>{ try { const config = await getOtpConfig(); if (config) { if (!propOtpLength) setOtpLength(config.otpLength); if (!propExpiredTime) setExpiredTime(Math.floor(config.expiredTime / 1000)); // Convert to seconds for timer } } catch (error) { // console.error('Failed to fetch OTP config:', error); // Keep defaults if fetch fails if (!propOtpLength) setOtpLength(6); if (!propExpiredTime) setExpiredTime(initialTimer || 120); } finally{ setIsConfigLoaded(true); } }; fetchOtpConfig(); } }, [ propOtpLength, propExpiredTime, initialTimer ]); // Handle OTP input change const handleOtpChange = (otp)=>{ setCurrentOtp(otp); }; // Handle OTP completion const handleOtpComplete = (otp)=>{ setCurrentOtp(otp); }; // Handle timer expiration const handleTimerExpired = ()=>{ setIsTimedOut(true); }; // Handle resend code const handleResendCode = async ()=>{ return resendNewOtp(); }; // Handle submit const handleSubmit = async ()=>{ if (isTimedOut || currentOtp.length < otpLength) return; await submit(); }; // Don't render until config is loaded to prevent layout shifts if (!isConfigLoaded) { return /*#__PURE__*/ _jsx("div", { className: `otp ${className}`, children: /*#__PURE__*/ _jsx("div", { className: "otp__container", children: /*#__PURE__*/ _jsx("div", { className: "otp__loading", children: "Loading..." }) }) }); } return /*#__PURE__*/ _jsx("div", { className: `otp ${className}`, children: /*#__PURE__*/ _jsxs("div", { className: "otp__container", children: [ /*#__PURE__*/ _jsx(OTPInput, { length: otpLength, disabled: isTimedOut, onChange: handleOtpChange, onComplete: handleOtpComplete, className: "otp__input-group", onReset: resetKey > 0 ? ()=>{} : undefined }, `otp-${otpLength}`), /*#__PURE__*/ _jsxs("div", { className: "otp__timer-section", children: [ /*#__PURE__*/ _jsx(TimerCountdown, { initialTimer: expiredTime, onExpired: handleTimerExpired, onResend: handleResendCode, className: "otp__timer", expiredMessage: t("otp:expired_message"), activeMessage: t("otp:otp_info"), showResendButton: true }, resetKey), /*#__PURE__*/ _jsx(Button, { size: "large", onClick: handleSubmit, disabled: isTimedOut || currentOtp.length < otpLength, type: "button", "aria-label": "Submit OTP code", children: t("otp:submit") }) ] }) ] }) }); } export default OtpView; //# sourceMappingURL=otp-view.js.map