Auth Page - Copy this React, Tailwind Component to your project
Import { useState } from "react"; import { FiMail, FiLock, FiUser } from "react icons/fi"; import api from "../../api"; const AuthPage = () => { const [activeTab, setActiveTab] = useState("login"); const [formData, setFormData] = useState({ fullName: "", email: "", password: "", confirmPassword: "", }); const [errors, setErrors] = useState({}); const validateForm = () => { const newErrors = {}; if (activeTab === "register") { if (!formData.fullName) newErrors.fullName = "Full name is required"; if (formData.password !== formData.confirmPassword) { newErrors.confirmPassword = "Passwords do not match"; } } if (!formData.email) { newErrors.email = "Email is required"; } else if (!/\S+@\S+\.\S+/.test(formData.email)) { newErrors.email = "Email is invalid"; } if (!formData.password) { newErrors.password = "Password is required"; } else if (formData.password.length < 6) { newErrors.password = "Password must be at least 6 characters"; } return newErrors; }; const handleSubmit = async (e) => { e.preventDefault(); const newErrors = validateForm(); if (Object.keys(newErrors).length === 0) { // Promise trong JavaScript const response = await api.post("register", formData); console.log("Form submitted", formData); console.log("Server response", response); // Không xảy ra ngay lập tức, cần thời gian để xử lý // Có nhiều trạng thái: Pending, Fulfilled, Rejected } else { setErrors(newErrors); } }; const handleChange = (e) => { const { name, value } = e.target; setFormData((prev) => ({ ...prev, [name]: value, })); }; return ( <div className="min h screen bg gray 100 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 6 rounded lg shadow lg"> <div className="flex justify center"> <img className="h 12 w auto" src="https://images.unsplash.com/photo 1633409361618 c73427e4e206" alt="Logo" /> </div> <div className="flex border b border gray 200"> <button className={`flex 1 py 4 px 6 text center ${ activeTab === "login" ? "border b 2 border blue 500 text blue 500" : "text gray 500 hover:text gray 700" }`} onClick={() => setActiveTab("login")} > Login </button> <button className={`flex 1 py 4 px 6 text center ${ activeTab === "register" ? "border b 2 border blue 500 text blue 500" : "text gray 500 hover:text gray 700" }`} onClick={() => setActiveTab("register")} > Register </button> </div> <form onSubmit={handleSubmit} className="mt 8 space y 6"> {activeTab === "register" && ( <div> <label htmlFor="fullName" className="block text sm font medium text gray 700" > Full Name </label> <div className="mt 1 relative rounded md shadow sm"> <div className="absolute inset y 0 left 0 pl 3 flex items center pointer events none"> <FiUser className="h 5 w 5 text gray 400" /> </div> <input type="text" name="fullName" id="fullName" value={formData.fullName} onChange={handleChange} className={`block w full pl 10 pr 3 py 2 border ${ errors.fullName ? "border red 500" : "border gray 300" } rounded md focus:outline none focus:ring blue 500 focus:border blue 500`} /> </div> {errors.fullName && ( <p className="mt 2 text sm text red 600">{errors.fullName}</p> )} </div> )} <div> <label htmlFor="email" className="block text sm font medium text gray 700" > Email Address </label> <div className="mt 1 relative rounded md shadow sm"> <div className="absolute inset y 0 left 0 pl 3 flex items center pointer events none"> <FiMail className="h 5 w 5 text gray 400" /> </div> <input type="email" name="email" id="email" value={formData.email} onChange={handleChange} className={`block w full pl 10 pr 3 py 2 border ${ errors.email ? "border red 500" : "border gray 300" } rounded md focus:outline none focus:ring blue 500 focus:border blue 500`} /> </div> {errors.email && ( <p className="mt 2 text sm text red 600">{errors.email}</p> )} </div> <div> <label htmlFor="password" className="block text sm font medium text gray 700" > Password </label> <div className="mt 1 relative rounded md shadow sm"> <div className="absolute inset y 0 left 0 pl 3 flex items center pointer events none"> <FiLock className="h 5 w 5 text gray 400" /> </div> <input type="password" name="password" id="password" value={formData.password} onChange={handleChange} className={`block w full pl 10 pr 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`} /> </div> {errors.password && ( <p className="mt 2 text sm text red 600">{errors.password}</p> )} </div> {activeTab === "register" && ( <div> <label htmlFor="confirmPassword" className="block text sm font medium text gray 700" > Confirm Password </label> <div className="mt 1 relative rounded md shadow sm"> <div className="absolute inset y 0 left 0 pl 3 flex items center pointer events none"> <FiLock className="h 5 w 5 text gray 400" /> </div> <input type="password" name="confirmPassword" id="confirmPassword" value={formData.confirmPassword} onChange={handleChange} className={`block w full pl 10 pr 3 py 2 border ${ errors.confirmPassword ? "border red 500" : "border gray 300" } rounded md focus:outline none focus:ring blue 500 focus:border blue 500`} /> </div> {errors.confirmPassword && ( <p className="mt 2 text sm text red 600"> {errors.confirmPassword} </p> )} </div> )} <div> <button type="submit" className="w full flex justify center py 2 px 4 border border transparent rounded md shadow sm text sm font medium text white bg blue 600 hover:bg blue 700 focus:outline none focus:ring 2 focus:ring offset 2 focus:ring blue 500" > {activeTab === "login" ? "Sign In" : "Register"} </button> </div> {activeTab === "login" && ( <div className="text center"> <a href="#" className="font medium text blue 600 hover:text blue 500" > Forgot your password? </a> </div> )} </form> </div> </div> ); }; export default AuthPage;
