Notification System - Copy this React, Tailwind Component to your project
Import { useState } from "react"; import { FcGoogle } from "react icons/fc"; import { FaFacebook, FaEye, FaEyeSlash } from "react icons/fa"; import { motion, AnimatePresence } from "framer motion"; import ReCAPTCHA from "react google recaptcha"; const PasswordStrength = ({ password }) => { const getStrength = (pass) => { let score = 0; if (pass.length >= 8) score++; if (pass.match(/[A Z]/)) score++; if (pass.match(/[0 9]/)) score++; if (pass.match(/[^A Za z0 9]/)) score++; return score; }; const strength = getStrength(password); const getColor = () => { if (strength === 0) return "bg gray 200"; if (strength === 1) return "bg red 500"; if (strength === 2) return "bg yellow 500"; if (strength === 3) return "bg blue 500"; return "bg green 500"; }; return ( <div className="mt 2"> <div className="h 2 w full bg gray 200 rounded full"> <div className={`h full ${getColor()} rounded full transition all duration 300`} style={{ width: `${(strength / 4) * 100}%` }} ></div> </div> <p className="text xs mt 1 text gray 500"> {strength === 0 && "Enter password"} {strength === 1 && "Weak"} {strength === 2 && "Fair"} {strength === 3 && "Good"} {strength === 4 && "Strong"} </p> </div> ); }; const VerificationPage = ({ email, onResend }) => { return ( <div className="text center space y 6"> <h2 className="text 3xl font extrabold text gray 900">Verify your email</h2> <p className="text gray 600">We have sent a verification link to</p> <p className="font medium text gray 800">{email}</p> <p className="text gray 600">Please check your inbox and click the link to verify your account</p> <div className="pt 4"> <button onClick={onResend} className="text blue 600 hover:text blue 800 font medium" > Resend verification email </button> </div> </div> ); }; const AuthPage = () => { const [authState, setAuthState] = useState("login"); const [showPassword, setShowPassword] = useState(false); const [isVerificationSent, setIsVerificationSent] = useState(false); const [formData, setFormData] = useState({ email: "", password: "", confirmPassword: "", fullName: "", username: "", phone: "", rememberMe: false, termsAccepted: false }); const [errors, setErrors] = useState({}); const [captchaValue, setCaptchaValue] = useState(null); const handleCaptchaChange = (value) => { setCaptchaValue(value); }; const validateEmail = (email) => { return email.match(/^[^\s@]+@[^\s@]+\.[^\s@]+$/); }; const validatePassword = (password) => { const hasMinLength = password.length >= 8; const hasUpperCase = /[A Z]/.test(password); const hasNumber = /[0 9]/.test(password); const hasSpecialChar = /[^A Za z0 9]/.test(password); return hasMinLength && hasUpperCase && hasNumber && hasSpecialChar; }; const handleInputChange = (e) => { const { name, value, type, checked } = e.target; setFormData(prev => ({ ...prev, [name]: type === "checkbox" ? checked : value })); }; const handleSubmit = (e) => { e.preventDefault(); const newErrors = {}; if (!validateEmail(formData.email)) { newErrors.email = "Please enter a valid email address"; } if (authState !== "forgot" && !validatePassword(formData.password)) { newErrors.password = "Password must be at least 8 characters long and include uppercase, number, and special character"; } if (authState === "register") { if (formData.password !== formData.confirmPassword) { newErrors.confirmPassword = "Passwords do not match"; } if (!formData.fullName) newErrors.fullName = "Full name is required"; if (!formData.username) newErrors.username = "Username is required"; if (!formData.phone) newErrors.phone = "Phone number is required"; if (!formData.termsAccepted) newErrors.terms = "Please accept terms and conditions"; } setErrors(newErrors); if (Object.keys(newErrors).length === 0) { if (authState === "register" || authState === "forgot") { setIsVerificationSent(true); } else { console.log("Form submitted:", formData); } } }; const handleResendVerification = () => { console.log("Resending verification email to:", formData.email); }; const isFormValid = () => { if (authState === "login") { return validateEmail(formData.email) && captchaValue; } else if (authState === "register") { return validateEmail(formData.email) && validatePassword(formData.password) && formData.password === formData.confirmPassword && formData.fullName && formData.username && formData.phone && formData.termsAccepted; } else { return validateEmail(formData.email); } }; return ( <div className="min h screen bg gray 50 flex items center justify center px 4 sm:px 6 lg:px 8"> <div className="max w md w full space y 8 bg white p 8 rounded xl shadow lg"> <AnimatePresence mode="wait"> <motion.div key={isVerificationSent ? "verification" : authState} initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }} exit={{ opacity: 0, y: 20 }} transition={{ duration: 0.3 }} > {isVerificationSent ? ( <VerificationPage email={formData.email} onResend={handleResendVerification} /> ) : ( <> <div className="text center"> <h2 className="mt 6 text 3xl font extrabold text gray 900"> {authState === "login" ? "Sign in to your account" : authState === "register" ? "Create your account" : "Reset your password"} </h2> </div> <form className="mt 8 space y 6" onSubmit={handleSubmit}> {authState === "register" && ( <> <div> <label className="block text sm font medium text gray 700">Full Name</label> <input type="text" name="fullName" value={formData.fullName} onChange={handleInputChange} className={`mt 1 block w full px 3 py 2 border ${errors.fullName ? 'border red 500' : 'border gray 300'} rounded md shadow sm focus:outline none focus:ring blue 500 focus:border blue 500`} /> {errors.fullName && <p className="mt 1 text sm text red 500">{errors.fullName}</p>} </div> <div> <label className="block text sm font medium text gray 700">Username</label> <input type="text" name="username" value={formData.username} onChange={handleInputChange} className={`mt 1 block w full px 3 py 2 border ${errors.username ? 'border red 500' : 'border gray 300'} rounded md shadow sm focus:outline none focus:ring blue 500 focus:border blue 500`} /> {errors.username && <p className="mt 1 text sm text red 500">{errors.username}</p>} </div> <div> <label className="block text sm font medium text gray 700">Phone Number</label> <input type="tel" name="phone" value={formData.phone} onChange={handleInputChange} className={`mt 1 block w full px 3 py 2 border ${errors.phone ? 'border red 500' : 'border gray 300'} rounded md shadow sm focus:outline none focus:ring blue 500 focus:border blue 500`} /> {errors.phone && <p className="mt 1 text sm text red 500">{errors.phone}</p>} </div> </> )} <div> <label className="block text sm font medium text gray 700">Email address</label> <input type="email" name="email" value={formData.email} onChange={handleInputChange} className={`mt 1 block w full px 3 py 2 border ${errors.email ? 'border red 500' : 'border gray 300'} rounded md shadow sm focus:outline none focus:ring blue 500 focus:border blue 500`} /> {errors.email && <p className="mt 1 text sm text red 500">{errors.email}</p>} </div> {authState !== "forgot" && ( <div className="relative"> <label className="block text sm font medium text gray 700">Password</label> <div className="mt 1 relative rounded md shadow sm"> <input type={showPassword ? "text" : "password"} name="password" value={formData.password} onChange={handleInputChange} className={`block w full px 3 py 2 border ${errors.password ? 'border red 500' : 'border gray 300'} rounded md focus:outline none focus:ring blue 500 focus:border blue 500`} /> <button type="button" className="absolute inset y 0 right 0 pr 3 flex items center" onClick={() => setShowPassword(!showPassword)} > {showPassword ? <FaEyeSlash className="text gray 400" /> : <FaEye className="text gray 400" />} </button> </div> {authState === "register" && <PasswordStrength password={formData.password} />} {errors.password && <p className="mt 1 text sm text red 500">{errors.password}</p>} </div> )} {authState === "register" && ( <div> <label className="block text sm font medium text gray 700">Confirm Password</label> <input type="password" name="confirmPassword" value={formData.confirmPassword} onChange={handleInputChange} className={`mt 1 block w full px 3 py 2 border ${errors.confirmPassword ? 'border red 500' : 'border gray 300'} rounded md shadow sm focus:outline none focus:ring blue 500 focus:border blue 500`} /> {errors.confirmPassword && <p className="mt 1 text sm text red 500">{errors.confirmPassword}</p>} </div> )} {authState === "login" && ( <div className="flex items center justify between"> <div className="flex items center"> <input type="checkbox" name="rememberMe" checked={formData.rememberMe} onChange={handleInputChange} className="h 4 w 4 text blue 600 focus:ring blue 500 border gray 300 rounded" /> <label className="ml 2 block text sm text gray 900">Remember me</label> </div> <button type="button" onClick={() => setAuthState("forgot")} className="text sm font medium text blue 600 hover:text blue 500" > Forgot your password? </button> </div> )} {authState === "register" && ( <div className="flex items center"> <input type="checkbox" name="termsAccepted" checked={formData.termsAccepted} onChange={handleInputChange} className="h 4 w 4 text blue 600 focus:ring blue 500 border gray 300 rounded" /> <label className="ml 2 block text sm text gray 900"> I accept the terms and conditions </label> </div> )} {authState === "login" && ( <div className="mt 4"> <ReCAPTCHA sitekey="6LeWqNkpAAAAANkqcg0zDmNz90pyG4FOLP4QiDQv" onChange={handleCaptchaChange} /> {errors.captcha && <p className="mt 1 text sm text red 500">{errors.captcha}</p>} </div> )} <button type="submit" disabled={!isFormValid()} className={`group relative w full flex justify center py 2 px 4 border border transparent text sm font medium rounded md text white ${isFormValid() ? 'bg blue 600 hover:bg blue 700' : 'bg gray 400 cursor not allowed'} focus:outline none focus:ring 2 focus:ring offset 2 focus:ring blue 500`} > {authState === "login" ? "Sign in" : authState === "register" ? "Sign up" : "Send reset link"} </button> {(authState === "login" || authState === "register") && ( <div className="mt 6"> <div className="relative"> <div className="absolute inset 0 flex items center"> <div className="w full border t border gray 300" /> </div> <div className="relative flex justify center text sm"> <span className="px 2 bg white text gray 500">Or continue with</span> </div> </div> <div className="mt 6 grid grid cols 2 gap 3"> <button type="button" className="w full inline flex justify center py 2 px 4 border border gray 300 rounded md shadow sm bg white text sm font medium text gray 500 hover:bg gray 50" > <FcGoogle className="h 5 w 5" /> <span className="ml 2">Google</span> </button> <button type="button" className="w full inline flex justify center py 2 px 4 border border gray 300 rounded md shadow sm bg white text sm font medium text gray 500 hover:bg gray 50" > <FaFacebook className="h 5 w 5 text blue 600" /> <span className="ml 2">Facebook</span> </button> </div> </div> )} </form> <div className="mt 6 text center"> <button type="button" onClick={() => setAuthState(authState === "login" ? "register" : "login")} className="text sm font medium text blue 600 hover:text blue 500" > {authState === "login" ? "Don't have an account? Sign up" : authState === "register" ? "Already have an account? Sign in" : "Back to sign in"} </button> </div> </> )} </motion.div> </AnimatePresence> </div> </div> ); }; export default AuthPage;(Tôi muốn quăng thông báo ra trước khi nào người dùng nhập đủ đáp ứng đủ điều kiện trong đó thì thông bao sẽ ẩn đi)
