fix: update registration flow to include user state management

This commit is contained in:
Thanh Quy - wolf
2026-04-19 18:52:33 +07:00
parent 48fc033ffa
commit 4eaed6c973
2 changed files with 81 additions and 17 deletions
+79 -17
View File
@@ -6,19 +6,20 @@ import { SHOP_INFO } from "@/lib/constants";
import Image from "next/image";
import Link from "next/link";
import { useRouter } from "next/navigation";
import { SubmitEvent, useState } from "react";
import { FormEvent, 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 { completeRegistration } = useAuth();
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);
// Validate Vietnamese phone number
const validatePhone = (phoneNumber: string): boolean => {
@@ -28,7 +29,7 @@ export default function RegisterPage() {
return phoneRegex.test(phoneNumber);
};
const handlePhoneSubmit = (e: SubmitEvent<HTMLFormElement>) => {
const handlePhoneSubmit = async (e: FormEvent<HTMLFormElement>) => {
e.preventDefault();
if (!phone.trim()) {
@@ -44,12 +45,34 @@ export default function RegisterPage() {
return;
}
// Move to OTP step
setStep("otp");
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 data = await res.json().catch(() => ({}));
const msg =
data?.error === "ExistedUser"
? "Số điện thoại đã được đăng ký"
: "Có lỗi xảy ra, vui lòng thử lại";
setErrors({ phone: msg, otp: "" });
}
} catch {
setErrors({ phone: "Không thể kết nối, vui lòng thử lại", otp: "" });
} finally {
setIsLoading(false);
}
};
const handleOtpSubmit = (e: SubmitEvent<HTMLFormElement>) => {
const handleOtpSubmit = async (e: FormEvent<HTMLFormElement>) => {
e.preventDefault();
if (!otp.trim()) {
@@ -57,14 +80,34 @@ export default function RegisterPage() {
return;
}
if (otp !== DEMO_OTP) {
setErrors({ ...errors, otp: "Mã OTP không đúng" });
return;
}
setIsLoading(true);
setErrors({ phone: "", otp: "" });
// Complete registration
completeRegistration(phone);
router.push("/");
try {
const res = await fetch("/api/customer/quickLogin", {
method: "POST",
headers: { "Content-Type": "application/json" },
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 data = await res.json().catch(() => ({}));
const msg =
data?.error === "InvalidOTP"
? "Mã OTP không đúng"
: "Có lỗi xảy ra, vui lòng thử lại";
setErrors({ phone: "", otp: msg });
}
} catch {
setErrors({ phone: "", otp: "Không thể kết nối, vui lòng thử lại" });
} finally {
setIsLoading(false);
}
};
const handleBackToPhone = () => {
@@ -144,7 +187,8 @@ export default function RegisterPage() {
setErrors({ ...errors, phone: "" });
}}
placeholder="0987654321"
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) lg:pl-11 ${errors.phone ? "border-red-400" : "border-(--color-border)"} `}
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)"} `}
/>
</div>
{errors.phone && (
@@ -166,8 +210,16 @@ export default function RegisterPage() {
type="submit"
style="login"
size="lg"
disabled={isLoading}
>
Tiếp tục
{isLoading ? (
<>
<i className="fa-solid fa-spinner fa-spin mr-2"></i>
Đang xử ...
</>
) : (
"Tiếp tục"
)}
</Button>
{/* Back to Login */}
@@ -218,7 +270,8 @@ export default function RegisterPage() {
}}
placeholder="Nhập mã OTP"
maxLength={6}
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) lg:pl-11 ${errors.otp ? "border-red-400" : "border-(--color-border)"} `}
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)"} `}
/>
</div>
{errors.otp && (
@@ -237,8 +290,16 @@ export default function RegisterPage() {
type="submit"
style="login"
size="lg"
disabled={isLoading}
>
Hoàn tất đăng
{isLoading ? (
<>
<i className="fa-solid fa-spinner fa-spin mr-2"></i>
Đang xử ...
</>
) : (
"Hoàn tất đăng ký"
)}
</Button>
{/* Back Button */}
@@ -247,6 +308,7 @@ export default function RegisterPage() {
onClick={handleBackToPhone}
size="lg"
style="login"
disabled={isLoading}
>
Thay đi số điện thoại
</Button>
+2
View File
@@ -12,6 +12,7 @@ import type { User } from "./types";
interface AuthContextType {
user: User | null;
setUser: (user: User | null) => void;
login: (username: string, password: string) => Promise<boolean>;
logout: () => void;
registerPhone: string | null;
@@ -170,6 +171,7 @@ export function AuthProvider({ children }: { children: ReactNode }) {
<AuthContext.Provider
value={{
user,
setUser,
login,
logout,
registerPhone,