"use client"; import Button from "@/components/atoms/buttons/Button"; import { useAuth } from "@/lib/auth-context"; import { SHOP_INFO } from "@/lib/constants"; import Image from "next/image"; import Link from "next/link"; import { useRouter } from "next/navigation"; import { FormEvent, useEffect, useState } from "react"; // Static OTP for demo (in production, this would be sent via SMS) const DEMO_OTP = "123456"; export default function RegisterPage() { const router = useRouter(); const { setUser } = useAuth(); const [step, setStep] = useState<"phone" | "otp">("phone"); const [phone, setPhone] = useState(""); const [otp, setOtp] = useState(""); const [errors, setErrors] = useState({ phone: "", otp: "" }); const [isLoading, setIsLoading] = useState(false); const [otpSending, setOtpSending] = useState(false); const [otpSendError, setOtpSendError] = useState(""); const sendOtp = async () => { setOtpSending(true); setOtpSendError(""); try { const res = await fetch("/api/sms_otp", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ phone }), }); if (!res.ok) { setOtpSendError("Unable to send OTP, please try again"); } } catch { setOtpSendError("Unable to connect, please try again"); } finally { setOtpSending(false); } }; useEffect(() => { if (step === "otp") { sendOtp(); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [step]); // Validate Vietnamese phone number const validatePhone = (phoneNumber: string): boolean => { // Vietnamese phone format: 10 digits starting with 0 // Valid prefixes: 03, 05, 07, 08, 09 const phoneRegex = /^(0[3|5|7|8|9])[0-9]{8}$/; return phoneRegex.test(phoneNumber); }; const handlePhoneSubmit = async (e: FormEvent) => { e.preventDefault(); if (!phone.trim()) { setErrors({ ...errors, phone: "Please enter your phone number" }); return; } if (!validatePhone(phone)) { setErrors({ ...errors, phone: "Invalid phone number (e.g. 0987654321)", }); return; } setIsLoading(true); setErrors({ phone: "", otp: "" }); try { const res = await fetch("/api/customer/quick_signup", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ phone }), }); if (res.ok) { setStep("otp"); } else { const errorCode = (await res.text().catch(() => "")).trim(); const phoneErrorMap: Record = { ExistedUser: "Phone number already registered", InvalidPhoneNumber: "Invalid phone number", }; const msg = phoneErrorMap[errorCode] ?? "An error occurred, please try again"; setErrors({ phone: msg, otp: "" }); } } catch { setErrors({ phone: "Unable to connect, please try again", otp: "" }); } finally { setIsLoading(false); } }; const handleOtpSubmit = async (e: FormEvent) => { e.preventDefault(); if (!otp.trim()) { setErrors({ ...errors, otp: "Please enter your OTP code" }); return; } setIsLoading(true); setErrors({ phone: "", otp: "" }); try { const res = await fetch("/api/customer/quick_login", { method: "POST", headers: { "Content-Type": "application/json" }, credentials: "include", body: JSON.stringify({ phone, otp }), }); if (res.ok) { const userData = await res.json(); setUser(userData); localStorage.setItem("coffee-shop-user", JSON.stringify(userData)); router.push("/"); } else { const errorCode = (await res.text().catch(() => "")).trim(); const msg = errorCode === "InvalidOTP" ? "Incorrect or expired OTP code" : "An error occurred, please try again"; setErrors({ phone: "", otp: msg }); } } catch { setErrors({ phone: "", otp: "Unable to connect, please try again" }); } finally { setIsLoading(false); } }; const handleBackToPhone = () => { setStep("phone"); setOtp(""); setErrors({ phone: "", otp: "" }); }; return (
{/* Register Form Card */}
{/* Logo & Shop Name */}
{SHOP_INFO.name}

{SHOP_INFO.name}

{step === "phone" ? "Create a customer account" : "Verify your phone number"}

{/* Step Indicator */}
1
2
{/* Phone Step */} {step === "phone" && (
{/* Phone Input */}
{ setPhone(e.target.value); setErrors({ ...errors, phone: "" }); }} placeholder="0987654321" disabled={isLoading} className={`text-foreground focus:ring-opacity-20 w-full rounded-xl border bg-white px-10 py-3 transition-all duration-150 outline-none placeholder:text-(--color-text-muted) focus:border-(--color-primary) focus:ring-2 focus:ring-(--color-primary) disabled:opacity-60 lg:pl-11 ${errors.phone ? "border-red-400" : "border-(--color-border)"} `} />
{errors.phone && (

{errors.phone}

)}

Enter a Vietnamese phone number (10 digits, starting with 0)

{/* Buttons */}
{/* Submit Button */} {/* Back to Login */} Back to sign in
)} {/* OTP Step */} {step === "otp" && (
{/* Info Message */}

{otpSending ? ( <> Sending OTP to {phone}... ) : ( <> OTP code sent to {phone} )}

Demo OTP:{" "} {DEMO_OTP}

{otpSendError && (

{otpSendError}

)} {/* OTP Input */}
{ setOtp(e.target.value); setErrors({ ...errors, otp: "" }); }} placeholder="Enter OTP code" maxLength={6} disabled={isLoading} className={`text-foreground focus:ring-opacity-20 w-full rounded-xl border bg-white px-4 py-3 text-center text-lg tracking-widest transition-all duration-150 outline-none placeholder:text-sm placeholder:tracking-normal placeholder:text-(--color-text-muted) focus:border-(--color-primary) focus:ring-2 focus:ring-(--color-primary) disabled:opacity-60 lg:pl-11 ${errors.otp ? "border-red-400" : "border-(--color-border)"} `} />
{errors.otp && (

{errors.otp}

)}
{/* Buttons */}
{/* Submit Button */} {/* Back Button */}
{/* Resend OTP */}
)}
); }