| |
| import { useState } from "react"; |
| import { useNavigate } from "react-router-dom"; |
| import api from "../api/client"; |
|
|
| export default function Login() { |
| const [step, setStep] = useState(1); |
| const [email, setEmail] = useState("student1@example.com"); |
| const [otp, setOtp] = useState(""); |
| const [serverOtp, setServerOtp] = useState(""); |
| const [loading, setLoading] = useState(false); |
| const [message, setMessage] = useState(""); |
| const navigate = useNavigate(); |
|
|
| async function handleRequestOtp(e) { |
| e.preventDefault(); |
| setLoading(true); |
| setMessage(""); |
| try { |
| const res = await api.post("/auth/request-otp", { email }); |
| if (res.data?.debug_code) { |
| setServerOtp(res.data.debug_code); |
| setMessage( |
| `Test OTP (email not configured): ${res.data.debug_code}` |
| ); |
| } else { |
| setMessage("OTP sent to your email address."); |
| } |
| setStep(2); |
| } catch (err) { |
| setMessage("Could not send OTP. Please try again."); |
| } finally { |
| setLoading(false); |
| } |
| } |
|
|
| async function handleVerifyOtp(e) { |
| e.preventDefault(); |
| setLoading(true); |
| setMessage(""); |
| try { |
| const res = await api.post("/auth/login", { email, otp }); |
| const payload = { |
| email: res.data.email, |
| name: res.data.name, |
| }; |
|
|
| |
| |
| localStorage.setItem("karateStudent", JSON.stringify(payload)); |
|
|
| navigate("/student"); |
| } catch (err) { |
| setMessage("Invalid or expired OTP. Please try again."); |
| } finally { |
| setLoading(false); |
| } |
| } |
|
|
| return ( |
| <div className="min-h-screen bg-slate-50 text-slate-900 flex items-center justify-center px-4"> |
| <div className="max-w-md w-full"> |
| <div className="mb-8 text-center"> |
| <h1 className="text-3xl font-semibold tracking-tight"> |
| Arun Martial Arts Student Portal |
| </h1> |
| <p className="mt-2 text-sm text-slate-500"> |
| Log in with your email. We’ll send you a one-time code. |
| </p> |
| </div> |
| <div className="bg-white shadow-xl rounded-2xl border border-slate-100 p-8"> |
| <h2 className="text-lg font-semibold mb-1"> |
| {step === 1 ? "Student Login" : "Enter OTP"} |
| </h2> |
| <p className="text-xs text-slate-500 mb-6"> |
| {step === 1 |
| ? "Type your email address and we’ll send you a one-time code." |
| : "Check the code below (for testing) or your inbox, and confirm login."} |
| </p> |
| |
| {step === 1 && ( |
| <form className="space-y-4" onSubmit={handleRequestOtp}> |
| <div> |
| <label className="block text-xs font-medium text-slate-600 mb-1"> |
| Email |
| </label> |
| <input |
| type="email" |
| required |
| value={email} |
| onChange={(e) => setEmail(e.target.value)} |
| className="w-full rounded-lg border border-slate-200 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 bg-white" |
| /> |
| </div> |
| <button |
| type="submit" |
| disabled={loading} |
| className="w-full inline-flex items-center justify-center rounded-lg bg-blue-600 text-white text-sm font-medium py-2.5 hover:bg-blue-700 disabled:opacity-60" |
| > |
| {loading ? "Sending…" : "Send OTP"} |
| </button> |
| </form> |
| )} |
| |
| {step === 2 && ( |
| <form className="space-y-4" onSubmit={handleVerifyOtp}> |
| <div> |
| <label className="block text-xs font-medium text-slate-600 mb-1"> |
| Email |
| </label> |
| <input |
| type="email" |
| value={email} |
| disabled |
| className="w-full rounded-lg border border-slate-100 px-3 py-2 text-sm bg-slate-50 text-slate-500" |
| /> |
| </div> |
| <div> |
| <label className="block text-xs font-medium text-slate-600 mb-1"> |
| One-time code |
| </label> |
| <input |
| type="text" |
| required |
| value={otp} |
| onChange={(e) => setOtp(e.target.value)} |
| className="w-full rounded-lg border border-slate-200 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 bg-white tracking-[0.35em]" |
| placeholder="••••••" |
| /> |
| </div> |
| {serverOtp && ( |
| <p className="text-[11px] text-amber-600 bg-amber-50 border border-amber-100 px-3 py-2 rounded-lg"> |
| Sandbox hint: your OTP is{" "} |
| <span className="font-mono font-semibold">{serverOtp}</span> |
| </p> |
| )} |
| <button |
| type="submit" |
| disabled={loading} |
| className="w-full inline-flex items-center justify-center rounded-lg bg-blue-600 text-white text-sm font-medium py-2.5 hover:bg-blue-700 disabled:opacity-60" |
| > |
| {loading ? "Verifying…" : "Log in"} |
| </button> |
| </form> |
| )} |
| |
| {message && ( |
| <p className="mt-4 text-xs text-slate-600 whitespace-pre-line"> |
| {message} |
| </p> |
| )} |
| </div> |
| </div> |
| </div> |
| ); |
| } |
|
|