Default Component - Copy this React, Tailwind Component to your project
Import { useState, ChangeEvent, FormEvent } from "react"; import { FaEye, FaEyeSlash } from "react icons/fa"; import PhoneInput from "react phone input 2"; import "react phone input 2/lib/style.css"; import { useNavigate } from "react router dom"; import { ToastContainer, toast, Zoom } from "react toastify"; import "react toastify/dist/ReactToastify.css"; import "./RegisterForm.scss"; import { FormData, FormErrors } from "../../types/RegisterForm"; const vietnamPhoneRegex = /^0(3|5|7|8|9)[0 9]{8}$/; const RegistrationForm: React.FC = () => { const [formData, setFormData] = useState<FormData>({ fullName: "", username: "", DoB: "", email: "", password: "", passwordConfirmation: "", role: "", phone: "", address: "", }); const [errors, setErrors] = useState<FormErrors>({}); const [showPassword, setShowPassword] = useState<boolean>(false); const [isLoading, setIsLoading] = useState<boolean>(false); const [passwordStrength, setPasswordStrength] = useState<number>(0); const navigate = useNavigate(); const validatePassword = (password: string): number => { let strength = 0; if (password.length >= 8) strength += 1; if (/[A Z]/.test(password)) strength += 1; if (/[0 9]/.test(password)) strength += 1; if (/[^A Za z0 9]/.test(password)) strength += 1; return strength; }; const validateForm = (): boolean => { const newErrors: FormErrors = {}; const { fullName, username, email, password, passwordConfirmation, role, phone, address, DoB, } = formData; if (!/^[A Za zÀ ỹ\s]{2,}$/.test(fullName)) { newErrors.fullName = "Tên chỉ được chứa chữ cái và ít nhất 2 ký tự"; } if (!/^[a zA Z0 9_]{4,}$/.test(username)) { newErrors.username = "Tên người dùng phải có ít nhất 4 ký tự (không dấu, không khoảng trắng)"; } if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) { newErrors.email = "Vui lòng nhập địa chỉ email hợp lệ"; } if (password.length < 8) { newErrors.password = "Mật khẩu phải có ít nhất 8 ký tự"; } if (password !== passwordConfirmation) { newErrors.passwordConfirmation = "Mật khẩu xác nhận không khớp"; } if (!role) { newErrors.role = "Vui lòng chọn vai trò"; } if (!phone || !vietnamPhoneRegex.test(phone)) { newErrors.phone = "Vui lòng nhập số điện thoại hợp lệ"; } if (address.length < 5) { newErrors.address = "Địa chỉ phải có ít nhất 5 ký tự"; } if (!DoB) { newErrors.DoB = "Vui lòng nhập ngày sinh"; } setErrors(newErrors); return Object.keys(newErrors).length === 0; }; const handleSubmit = async (e: FormEvent<HTMLFormElement>) => { e.preventDefault(); if (validateForm()) { setIsLoading(true); try { const apiUrl = formData.username.includes("test") ? "https://f113a0ea c6c6 48f4 b55b e526c80aefaa.mock.pstmn.io/api/registerfail" : "https://f113a0ea c6c6 48f4 b55b e526c80aefaa.mock.pstmn.io/api/register"; const response = await fetch(apiUrl, { method: "POST", headers: { "Content Type": "application/json" }, body: JSON.stringify({ fullName: formData.fullName, username: formData.username, email: formData.email, password: formData.password, phone: formData.phone, address: formData.address, role: formData.role, DoB: formData.DoB, }), }); if (response.ok) { const result = await response.json(); toast.success(`${result.message}`, { position: "top center", autoClose: 3000, theme: "dark", pauseOnHover: false, pauseOnFocusLoss: false, closeOnClick: true, transition: Zoom, }); setFormData({ fullName: "", username: "", DoB: "", email: "", password: "", passwordConfirmation: "", role: "", phone: "", address: "", }); setTimeout(() => { navigate("/login"); }, 3000); } else { const errorData = await response.json(); toast.error(`${errorData.message}`, { position: "top center", autoClose: 3000, theme: "dark", pauseOnHover: false, pauseOnFocusLoss: false, closeOnClick: true, transition: Zoom, }); } } catch (error: any) { console.error("Đăng ký lỗi:", error); toast.error(error.message || "Đăng ký thất bại. Vui lòng thử lại.", { position: "top center", autoClose: 3000, theme: "dark", pauseOnHover: false, pauseOnFocusLoss: false, closeOnClick: true, transition: Zoom, }); } finally { setIsLoading(false); } } }; const handleChange = (e: ChangeEvent<HTMLInputElement | HTMLSelectElement>) => { const { name, value } = e.target; setFormData((prev) => ({ ...prev, [name]: value, })); if (name === "password") { setPasswordStrength(validatePassword(value)); } }; return ( <div className="registration container"> <div className="registration wrapper"> <div className="registration background"></div> <div className="registration form container"> <div className="registration form wrapper"> <div className="registration form content"> <h2 className="registration title">Đăng Ký Tài Khoản</h2> <form onSubmit={handleSubmit} className="registration form"> <div className="form group"> <label className="form label">Họ và Tên</label> <input type="text" name="fullName" value={formData.fullName} onChange={handleChange} placeholder="vd: Nguyễn Văn A" className={`form input ${errors.fullName ? "input error" : ""}`} /> {errors.fullName && ( <p className="error message">{errors.fullName}</p> )} </div> <div className="form group"> <label className="form label">Tên người dùng</label> <input type="text" name="username" value={formData.username} onChange={handleChange} placeholder="vd: nguyenvana" className={`form input ${errors.username ? "input error" : ""}`} /> {errors.username && ( <p className="error message">{errors.username}</p> )} </div> <div className="form group"> <label className="form label">Vai trò</label> <select name="role" value={formData.role} onChange={handleChange} className={`form input ${errors.role ? "input error" : ""}`} > <option value="">Chọn vai trò</option> <option value="student">Học sinh</option> <option value="parent">Phụ huynh</option> </select> {errors.role && <p className="error message">{errors.role}</p>} </div> <div className="form group"> <label className="form label">Ngày sinh</label> <input type="date" name="DoB" value={formData.DoB} onChange={handleChange} className={`form input ${errors.DoB ? "input error" : ""}`} max={new Date().toISOString().split("T")[0]} // Giới hạn không cho chọn ngày trong tương lai /> {errors.DoB && <p className="error message">{errors.DoB}</p>} </div> <div className="form group"> <label className="form label">Email</label> <input type="email" name="email" value={formData.email} onChange={handleChange} placeholder="vd: a@a.com" className={`form input ${errors.email ? "input error" : ""}`} /> {errors.email && <p className="error message">{errors.email}</p>} </div> <div className="form group"> <label className="form label">Số điện thoại</label> <PhoneInput country="vn" disableCountryCode autoFormat disableDropdown placeholder="vd: 0912345678" value={formData.phone} onChange={(phone) => { setFormData((prev) => ({ ...prev, phone })); if (!vietnamPhoneRegex.test(phone)) { setErrors((prev) => ({ ...prev, phone: "Số điện thoại không hợp lệ (phải có 10 hoặc 11 chữ số)", })); } else { setErrors((prev) => ({ ...prev, phone: "" })); } }} disableCountryGuess containerClass="phone input container" inputClass={`phone input ${errors.phone ? "input error" : ""}`} /> {errors.phone && <p className="error message">{errors.phone}</p>} </div> <div className="form group"> <label className="form label">Mật khẩu</label> <div className="password input container"> <input type={showPassword ? "text" : "password"} name="password" value={formData.password} onChange={handleChange} placeholder="Mật khẩu từ 8 ký tự" className={`form input ${errors.password ? "input error" : ""}`} /> <button type="button" onClick={() => setShowPassword(!showPassword)} className="password toggle button" > {showPassword ? <FaEyeSlash /> : <FaEye />} </button> </div> {errors.password && ( <p className="error message">{errors.password}</p> )} <p className={`password strength text ${passwordStrength === 4 ? "strong" : passwordStrength === 3 ? "medium" : "weak"}`} > {passwordStrength === 4 ? "Mật khẩu mạnh" : passwordStrength === 3 ? "Mật khẩu trung bình" : passwordStrength === 2 ? "Mật khẩu yếu" : ""} </p> </div> <div className="form group"> <label className="form label">Xác nhận mật khẩu</label> <input type="password" name="passwordConfirmation" value={formData.passwordConfirmation} onChange={handleChange} placeholder="Nhập lại mật khẩu" className={`form input ${errors.passwordConfirmation ? "input error" : ""}`} /> {errors.passwordConfirmation && ( <p className="error message">{errors.passwordConfirmation}</p> )} </div> <div className="form group"> <label className="form label">Địa chỉ</label> <input type="text" name="address" value={formData.address} onChange={handleChange} placeholder="vd: Số 12, đường ABC, quận XYZ" className={`form input ${errors.address ? "input error" : ""}`} /> {errors.address && <p className="error message">{errors.address}</p>} </div> <div className="form group"> <button type="submit" disabled={isLoading} className="submit button" > {isLoading ? "Đang xử lý..." : "Đăng ký"} </button> </div> </form> </div> </div> </div> </div> <ToastContainer /> </div> ); }; export default RegistrationForm; i want to redesign Dob relate codes and create a RegisterConfirmation page
