UNPKG

user-auth-pkg

Version:

A **highly customizable** and **reusable login form component** built with **React Hook Form**, **Yup**, and **Material UI**, supporting **image uploads**, **file inputs**, **CAPTCHA validation**, and flexible UI overrides.

102 lines (101 loc) 6.69 kB
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; import { useState, useRef, useEffect } from 'react'; import { useForm, Controller } from 'react-hook-form'; import { yupResolver } from '@hookform/resolvers/yup'; import axios from 'axios'; import ReCAPTCHA from 'react-google-recaptcha'; import { buildValidationSchema } from './validationSchema'; import { defaultFormConfig } from './defaultFormConfig'; const LoginForm = ({ config, onSuccess, onError, validationSchema, textFieldComponent: TextFieldComponent, imageFieldComponent: ImageFieldComponent, buttonComponent: ButtonComponent, selectFieldComponent: SelectFieldComponent, buttonText = 'Submit', isLoadingText = 'Processing...', errorText = 'Something went wrong.', axiosConfig, enableCaptcha = false, captchaSiteKey = '', }) => { const finalConfig = config && config.length > 0 ? config : defaultFormConfig; const [isLoading, setIsLoading] = useState(false); const [apiError, setApiError] = useState(''); const [captchaToken, setCaptchaToken] = useState(''); const [showCaptcha, setShowCaptcha] = useState(false); const captchaRef = useRef(null); const { register, handleSubmit, control, formState: { errors }, getValues, setValue, } = useForm({ resolver: yupResolver(validationSchema || buildValidationSchema(finalConfig)), mode: 'onChange', }); useEffect(() => { finalConfig.forEach((field) => { if (field.isFile || field.isImage) { register(field.name); } }); setValue('accountTypeId', 1); // You can change this based on your logic }, [register, finalConfig, setValue]); const handleCaptchaChange = async (token) => { if (token) { localStorage.setItem('captchaToken', token); setCaptchaToken(token); const formData = getValues(); console.log('Form Data Before Submit:', formData); // Check formData await doSubmit({ ...formData, captchaResponse: token }); } }; const handleImageChange = (e, fieldName) => { const file = e.target.files?.[0]; if (file) { setValue(fieldName, file, { shouldValidate: true }); } }; const doSubmit = async (formData) => { setIsLoading(true); setApiError(''); console.log('Captcha Token:', captchaToken); // Check if token exists try { const payload = {}; finalConfig.forEach((field) => { let value = formData[field.name]; if (field.type === 'number') value = value !== '' ? Number(value) : undefined; payload[field.name] = value; }); if (enableCaptcha) { payload.captchaResponse = localStorage.getItem('captchaToken') || captchaToken; } const method = (axiosConfig.method || 'post').toLowerCase(); const response = await axios({ ...axiosConfig, params: axiosConfig.params ?? (method === 'get' ? payload : undefined), data: axiosConfig.data ?? (method !== 'get' ? payload : undefined), }); onSuccess?.(response.data); } catch (err) { const message = err?.response?.data?.message || err?.message || errorText; setApiError(message); onError?.(message); } finally { setIsLoading(false); captchaRef.current?.reset(); setShowCaptcha(false); } }; const onSubmit = async (formData) => { enableCaptcha ? setShowCaptcha(true) : await doSubmit(formData); }; const renderField = (field, index) => { const fieldError = errors[field.name]?.message; const file = getValues(field.name); if (field.type === 'select' && field.options) { return SelectFieldComponent ? (_jsx(Controller, { name: field.name, control: control, defaultValue: "", render: ({ field: controllerField, fieldState }) => (_jsx(SelectFieldComponent, { label: field.label || field.placeholder, options: field.options, error: !!fieldState.error, helperText: fieldState.error?.message, ...controllerField })) })) : (_jsxs("div", { children: [_jsx("label", { children: field.label }), _jsxs("select", { ...register(field.name), defaultValue: "", children: [_jsx("option", { value: "", children: "Select" }), field.options.map((opt, idx) => (_jsx("option", { value: opt, children: opt }, idx)))] }), fieldError && _jsx("p", { style: { color: 'red' }, children: fieldError })] }, index)); } if (field.isFile || field.isImage) { return ImageFieldComponent ? (_jsx(ImageFieldComponent, { label: field.label, name: field.name, error: !!fieldError, helperText: fieldError, onChange: (e) => handleImageChange(e, field.name), preview: field.isImage && file ? URL.createObjectURL(file) : undefined })) : (_jsxs("div", { children: [_jsx("label", { children: field.label }), _jsx("input", { type: "file", onChange: (e) => handleImageChange(e, field.name) })] }, index)); } return TextFieldComponent ? (_jsx(TextFieldComponent, { type: field.type, placeholder: field.placeholder, error: !!errors[field.name], helperText: fieldError, ...register(field.name), fullWidth: true })) : (_jsxs("div", { children: [_jsx("input", { type: field.type, placeholder: field.placeholder, ...register(field.name) }), fieldError && _jsx("p", { style: { color: 'red' }, children: fieldError })] }, index)); }; return (_jsxs("form", { onSubmit: handleSubmit(onSubmit), children: [_jsx("div", { style: { display: 'flex', flexWrap: 'wrap', gap: '10px' }, children: finalConfig.map((field, i) => (_jsx("div", { style: { marginBottom: 16, width: finalConfig.length === 1 ? '100%' : finalConfig.length === 2 ? '100%' : '48%', boxSizing: 'border-box', }, children: field.name !== 'accountTypeId' && renderField(field, i) }, i))) }), showCaptcha && enableCaptcha && (_jsx(ReCAPTCHA, { sitekey: captchaSiteKey, ref: captchaRef, onChange: handleCaptchaChange })), !showCaptcha && (ButtonComponent ? (_jsx(ButtonComponent, { isLoading: isLoading, children: isLoading ? isLoadingText : buttonText })) : (_jsx("button", { type: "submit", disabled: isLoading, children: isLoading ? isLoadingText : buttonText }))), apiError && _jsx("p", { style: { color: 'red' }, children: apiError })] })); }; export default LoginForm;