update validate

This commit is contained in:
2026-03-31 00:19:52 +07:00
parent 5f1a29f9ef
commit 0ba4d43518

View File

@@ -23,10 +23,9 @@ export default function SignUpForm() {
const [errorMsg, setErrorMsg] = useState(""); const [errorMsg, setErrorMsg] = useState("");
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
// Hàm handle thay đổi input
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => { const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setFormData({ ...formData, [e.target.name]: e.target.value }); setFormData({ ...formData, [e.target.name]: e.target.value });
setErrorMsg(""); // Xoá lỗi khi user gõ lại setErrorMsg("");
}; };
// Hàm validate email // Hàm validate email
@@ -35,12 +34,18 @@ export default function SignUpForm() {
return emailRegex.test(email); return emailRegex.test(email);
}; };
// Hàm validate mật khẩu mới
const isValidPassword = (pass: string) => {
// Tối thiểu 8 ký tự, 1 chữ in hoa, 1 chữ số, 1 ký tự đặc biệt
const passwordRegex = /^(?=.*[A-Z])(?=.*\d)(?=.*[\W_]).{8,}$/;
return passwordRegex.test(pass);
};
// Xử lý khi bấm nút Sign Up (Step 1) // Xử lý khi bấm nút Sign Up (Step 1)
const handleSignUpClick = async (e: React.FormEvent<HTMLFormElement>) => { const handleSignUpClick = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault(); e.preventDefault();
setErrorMsg(""); setErrorMsg("");
// Validate cơ bản
if (!formData.fname || !formData.lname || !formData.email || !formData.password) { if (!formData.fname || !formData.lname || !formData.email || !formData.password) {
setErrorMsg("Vui lòng điền đầy đủ thông tin."); setErrorMsg("Vui lòng điền đầy đủ thông tin.");
return; return;
@@ -49,12 +54,14 @@ export default function SignUpForm() {
setErrorMsg("Email không đúng định dạng."); setErrorMsg("Email không đúng định dạng.");
return; return;
} }
if (!isValidPassword(formData.password)) {
setErrorMsg("Mật khẩu chưa đủ điều kiện. Vui lòng nhập tối thiểu 8 ký tự, 1 in hoa, 1 số và 1 ký tự đặc biệt.");
return;
}
try { try {
setLoading(true); setLoading(true);
// Gọi hàm CREATEOTP
await apiCreateOTP(formData.email); await apiCreateOTP(formData.email);
// Nếu thành công, chuyển sang màn hình OTP
setStep(2); setStep(2);
} catch (error) { } catch (error) {
setErrorMsg("Lỗi khi tạo OTP. Vui lòng thử lại."); setErrorMsg("Lỗi khi tạo OTP. Vui lòng thử lại.");
@@ -91,6 +98,7 @@ export default function SignUpForm() {
}; };
const signupRes = await apiSignUp(signupPayload); const signupRes = await apiSignUp(signupPayload);
console.log("API Sign Up Response:", signupRes);
console.log("Đăng ký thành công!", signupRes); console.log("Đăng ký thành công!", signupRes);
alert("Đăng ký thành công! Đang chuyển hướng..."); alert("Đăng ký thành công! Đang chuyển hướng...");
@@ -139,15 +147,12 @@ export default function SignUpForm() {
{/* ----- STEP 1: FORM SIGN UP ----- */} {/* ----- STEP 1: FORM SIGN UP ----- */}
{step === 1 && ( {step === 1 && (
<> <>
{/* Các nút đăng ký Social (Bỏ qua xử lý logic trong ví dụ này) */} {/* Các nút đăng ký Social */}
<div className="grid grid-cols-1 gap-3 sm:grid-cols-2 sm:gap-5"> <div className="grid grid-cols-1 gap-3 sm:grid-cols-2 sm:gap-5">
{/* ... (Giữ nguyên các nút Sign up with Google / X của bạn) ... */}
<button className="inline-flex items-center justify-center gap-3 py-3 text-sm font-normal text-gray-700 transition-colors bg-gray-100 rounded-lg px-7 hover:bg-gray-200 hover:text-gray-800 dark:bg-white/5 dark:text-white/90 dark:hover:bg-white/10"> <button className="inline-flex items-center justify-center gap-3 py-3 text-sm font-normal text-gray-700 transition-colors bg-gray-100 rounded-lg px-7 hover:bg-gray-200 hover:text-gray-800 dark:bg-white/5 dark:text-white/90 dark:hover:bg-white/10">
{/* Icon Google */}
Sign up with Google Sign up with Google
</button> </button>
<button className="inline-flex items-center justify-center gap-3 py-3 text-sm font-normal text-gray-700 transition-colors bg-gray-100 rounded-lg px-7 hover:bg-gray-200 hover:text-gray-800 dark:bg-white/5 dark:text-white/90 dark:hover:bg-white/10"> <button className="inline-flex items-center justify-center gap-3 py-3 text-sm font-normal text-gray-700 transition-colors bg-gray-100 rounded-lg px-7 hover:bg-gray-200 hover:text-gray-800 dark:bg-white/5 dark:text-white/90 dark:hover:bg-white/10">
{/* Icon X */}
Sign up with X Sign up with X
</button> </button>
</div> </div>
@@ -209,41 +214,41 @@ export default function SignUpForm() {
{/* Password */} {/* Password */}
<div> <div>
<Label> <Label>Password<span className="text-error-500">*</span></Label>
Password<span className="text-error-500">*</span>
</Label> {/* Thêm style báo đỏ ô nhập nếu pass chưa hợp lệ */}
<div className="relative"> <div className={`relative ${formData.password.length > 0 && !isValidPassword(formData.password) ? 'border border-red-500 ring-1 ring-red-500 rounded-lg' : ''}`}>
<Input <Input
name="password" name="password"
defaultValue={formData.password} defaultValue={formData.password}
onChange={handleChange} onChange={handleChange}
placeholder="Enter your password" placeholder="Min. 8 characters"
type={showPassword ? "text" : "password"} type={showPassword ? "text" : "password"}
/> />
<span <span onClick={() => setShowPassword(!showPassword)} className="absolute z-30 -translate-y-1/2 cursor-pointer right-4 top-1/2">
onClick={() => setShowPassword(!showPassword)} {showPassword ? <EyeIcon /> : <EyeCloseIcon />}
className="absolute z-30 -translate-y-1/2 cursor-pointer right-4 top-1/2"
>
{showPassword ? (
<span className="fill-gray-500 dark:fill-gray-400">
<EyeIcon />
</span>
) : (
<span className="fill-gray-500 dark:fill-gray-400">
<EyeCloseIcon />
</span>
)}
</span> </span>
</div> </div>
{/* Gợi ý trực quan cho người dùng */}
<p className={`mt-2 text-xs ${formData.password.length === 0 ? 'text-gray-400' : isValidPassword(formData.password) ? 'text-green-500' : 'text-red-500'}`}>
Mật khẩu phải chứa tối thiểu 8 tự, 1 chữ cái in hoa, 1 chữ số 1 tự đc biệt.
</p>
</div> </div>
{/* Checkbox */} {/* Checkbox */}
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<div className={formData.password.length > 0 && !isValidPassword(formData.password) ? "opacity-50 cursor-not-allowed" : ""}>
<Checkbox <Checkbox
className="w-5 h-5" className="w-5 h-5"
checked={isChecked} checked={isChecked}
onChange={setIsChecked} // Chặn bấm check nếu password chưa hợp lệ
onChange={(val) => {
if (isValidPassword(formData.password)) setIsChecked(val);
}}
disabled={!isValidPassword(formData.password)}
/> />
</div>
<p className="inline-block font-normal text-gray-500 dark:text-gray-400"> <p className="inline-block font-normal text-gray-500 dark:text-gray-400">
By creating an account means you agree to the{" "} By creating an account means you agree to the{" "}
<span className="text-gray-800 dark:text-white/90"> <span className="text-gray-800 dark:text-white/90">
@@ -260,9 +265,9 @@ export default function SignUpForm() {
<div> <div>
<button <button
type="submit" type="submit"
disabled={!isChecked || loading} disabled={!isChecked || loading || !isValidPassword(formData.password)}
className={`flex items-center justify-center w-full px-4 py-3 text-sm font-medium text-white transition rounded-lg bg-brand-500 shadow-theme-xs className={`flex items-center justify-center w-full px-4 py-3 text-sm font-medium text-white transition rounded-lg bg-brand-500 shadow-theme-xs
${!isChecked ? "opacity-50 cursor-not-allowed" : "hover:bg-brand-600"}`} ${(!isChecked || !isValidPassword(formData.password)) ? "opacity-50 cursor-not-allowed" : "hover:bg-brand-600"}`}
> >
{loading ? "Processing..." : "Sign Up"} {loading ? "Processing..." : "Sign Up"}
</button> </button>