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
JavaScript
'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