UNPKG

vfi-2fa

Version:

- Headless (100% customizable, Bring-your-own-UI) - Auto out of the box, fully controllable API - Sorting (Multi and Stable) - Filters - Row Selection - Row Expansion - Column Ordering - Animatable - Resizable - Server-side/controlled data/state

120 lines (119 loc) 9.14 kB
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; /* eslint-disable no-unused-vars */ import { useEffect, useRef, useState } from "react"; import { Button, Col, Collapse, Input, Modal, ModalBody, Row } from "reactstrap"; import ModalHeader from "./ModalHeader"; import { requester } from "../utils/requester"; const MaskHelper = { MaskEmail: (email) => { if (!email) { return ''; } const parts = email.split('@'); return `${parts[0].slice(0, 3)}***@${parts[1]}`; }, MaskPhone: (phone) => { if (!phone) { return ''; } return phone.replace(/\d(?=\d{4})/g, '*'); } }; export const OTPHandler = (props) => { const { isOpen, onSubmit, onClose, otpData, notificationError, sendEndpoint, email, phone } = props; const refOTP = useRef(null); const [otp, setOtp] = useState(""); const [secret, setSecret] = useState(); const [isOpenMore, setIsOpenMore] = useState(); const [selectedType, setSelectedType] = useState(otpData?.type); const [pendingType, setPendingType] = useState(otpData?.type); const [otpMessage, setOtpMessage] = useState(otpData?.message); const sendOTPApi = ({ params, endpoint }) => { return requester.post(endpoint, params); }; const isNeedSendOTP = () => { return pendingType !== "TOTP" && !secret || (pendingType !== selectedType); }; const handleSubmit = (e) => { e.preventDefault(); if (isNeedSendOTP()) { const params = { params: { type: pendingType }, endpoint: sendEndpoint }; sendOTPApi(params).then(response => { if (response && response.isValid) { setSelectedType(pendingType); setSecret(response.data?.secretData); switch (pendingType) { case "TOTP": setOtpMessage("Vui lòng kiểm tra ứng dụng bảo mật và nhập mã OTP của bạn."); break; case "SMS_OTP": const markPhone = MaskHelper.MaskPhone(phone || otpData?.user2FAConfigDto?.phone); setOtpMessage(markPhone ? `Mã bảo mật đã được gửi tới ${markPhone}. Vui lòng kiểm tra ứng dụng tin nhắn và nhập mã OTP của bạn.` : "Vui lòng kiểm tra ứng dụng tin nhắn và nhập mã OTP của bạn."); break; case "EMAIL_OTP": const markEmail = MaskHelper.MaskEmail(email || otpData?.user2FAConfigDto?.email); setOtpMessage(markEmail ? `Mã bảo mật đã được gửi tới ${markEmail}. Vui lòng kiểm tra OTP được gửi tới hòm thư của bạn.` : "Vui lòng kiểm tra OTP được gửi tới hòm thư của bạn."); break; } refOTP?.current?.focus(); setIsOpenMore(false); } else { setPendingType(selectedType); notificationError?.("Không thể lấy mã OTP, vui lòng thử lại sau"); } }).catch(e => { setPendingType(selectedType); notificationError?.("Không thể lấy mã OTP, vui lòng thử lại sau"); refOTP?.current?.focus(); }); } else { onSubmit(otp, secret, selectedType); } }; // Handle modal close const handleCancel = () => { }; const handleClosed = () => { setOtp(""); setSecret(""); setOtpMessage(""); }; const handleModalOpened = () => { setSelectedType(otpData?.type); setPendingType(otpData?.type); if (otpData?.message) { setOtpMessage(otpData?.message); } setOtp(''); refOTP?.current?.focus(); }; useEffect(() => { setSelectedType(otpData?.type); setPendingType(otpData?.type); setOtp(''); }, [otpData]); return (_jsxs(Modal, { className: 'modal-dialog modal-dialog-centered', isOpen: isOpen, // toggle={handleCancel} contentClassName: 'p-0', onOpened: handleModalOpened, onClosed: handleClosed, // backdrop={"static"} keyboard: true, children: [_jsx(ModalHeader, { title: 'X\u00E1c minh b\u1EA3o m\u1EADt', handleModal: onClose }), _jsxs(ModalBody, { children: [selectedType === 'TOTP' && (_jsx("div", { className: "mb-1", children: _jsxs("p", { children: ["Vui l\u00F2ng nh\u1EADp m\u00E3 OTP \u0111\u00E3 \u0111\u01B0\u1EE3c thi\u1EBFt l\u1EADp trong \u1EE9ng d\u1EE5ng x\u00E1c th\u1EF1c c\u1EE7a b\u1EA1n (", _jsx("span", { className: "fw-bold text-primary", children: "Google Authenticator" }), ", ", ' ', _jsx("span", { className: "fw-bold text-primary", children: "Microsoft Authenticator" }), ",", ' ', _jsx("span", { className: "fw-bold text-primary", children: "Authy" }), ",", ' ', _jsx("span", { className: "fw-bold text-primary", children: "1Password" }), ",...)"] }) })), selectedType === 'EMAIL_OTP' && (_jsxs("div", { className: "mb-1", children: ["Vui l\u00F2ng nh\u1EADp m\u00E3 OTP \u0111\u00E3 \u0111\u01B0\u1EE3c g\u1EEDi \u0111\u1EBFn email c\u1EE7a b\u1EA1n \u0111\u1EC3 x\u00E1c minh b\u1EA3o m\u1EADt v\u00E0 ti\u1EBFp t\u1EE5c thao t\u00E1c.", _jsx("br", {})] })), selectedType === 'SMS_OTP' && (_jsxs("div", { className: "mb-1", children: ["Vui l\u00F2ng nh\u1EADp m\u00E3 OTP \u0111\u00E3 \u0111\u01B0\u1EE3c g\u1EEDi \u0111\u1EBFn s\u1ED1 \u0111i\u1EC7n tho\u1EA1i c\u1EE7a b\u1EA1n \u0111\u1EC3 x\u00E1c minh b\u1EA3o m\u1EADt v\u00E0 ti\u1EBFp t\u1EE5c thao t\u00E1c.", _jsx("br", {})] })), _jsxs(Row, { className: 'gy-1', children: [_jsx(Col, { xs: 12, children: _jsx(Input, { innerRef: refOTP, placeholder: 'Nh\u1EADp m\u00E3 x\u00E1c th\u1EF1c', autoComplete: 'off', className: "py-1 px-1 fs-3", maxLength: 6, minLength: 6, autoFocus: true, value: otp, onChange: (e) => { setOtp(e.currentTarget.value); } }) }), otpMessage && (_jsx(Col, { xs: 12, children: _jsx("div", { className: secret ? "text-primary" : "text-warning", children: otpMessage }) })), _jsx("hr", {}), _jsxs(Col, { xs: 12, children: [_jsx("a", { onClick: () => setIsOpenMore(!isOpenMore), children: "Ho\u1EB7c th\u1EED v\u1EDBi c\u00E1c ph\u01B0\u01A1ng th\u1EE9c kh\u00E1c" }), _jsxs(Collapse, { isOpen: isOpenMore, children: [otpData?.user2FAConfigDto?.enableTOTP && (_jsxs("div", { className: "form-check", children: [_jsx("input", { className: "form-check-input", type: "radio", name: "Type", value: "TOTP", id: "flexRadioTOTP", checked: pendingType === 'TOTP', onChange: (e) => { setPendingType(e.currentTarget.value); } }), _jsx("label", { className: "form-check-label", htmlFor: "flexRadioTOTP", children: "\u1EE8ng d\u1EE5ng x\u00E1c th\u1EF1c" })] })), otpData?.user2FAConfigDto?.enableEmail && (_jsxs("div", { className: "form-check", children: [_jsx("input", { className: "form-check-input", type: "radio", name: "Type", value: "EMAIL_OTP", id: "flexRadioEmail", checked: pendingType === 'EMAIL_OTP', onChange: (e) => { setPendingType(e.currentTarget.value); } }), _jsxs("label", { className: "form-check-label", htmlFor: "flexRadioEmail", children: ["Email ", MaskHelper.MaskEmail(email ?? otpData?.user2FAConfigDto?.email)] })] })), otpData?.user2FAConfigDto?.enableSms && (_jsxs("div", { className: "form-check", children: [_jsx("input", { className: "form-check-input", type: "radio", name: "Type", value: "SMS_OTP", id: "flexRadioSMS", checked: pendingType === 'SMS_OTP', onChange: (e) => { setPendingType(e.currentTarget.value); } }), _jsxs("label", { className: "form-check-label", htmlFor: "flexRadioSMS", children: ["S\u1ED1 \u0111i\u1EC7n tho\u1EA1i ", MaskHelper.MaskPhone(phone || otpData?.user2FAConfigDto?.phone)] })] }))] })] }), _jsxs(Col, { className: 'd-flex justify-content-end mt-2', xs: 12, children: [_jsx(Button, { outline: true, color: 'secondary', className: 'me-1', onClick: onClose, children: "H\u1EE7y" }), isNeedSendOTP() ? (_jsx(Button, { color: 'primary', className: '', onClick: handleSubmit, outline: true, children: _jsx("span", { className: 'me-50', children: "G\u1EEDi OTP" }) })) : (_jsx(Button, { color: 'primary', className: '', onClick: handleSubmit, children: _jsx("span", { className: 'me-50', children: "X\u00E1c th\u1EF1c" }) }))] })] })] })] })); };