"use client"; import React, { useEffect, useState } from "react"; import { TextInput, Button, Title, Paper, Group, Text, List, } from "@mantine/core"; import { notifications } from "@mantine/notifications"; import { IconUser, IconRefresh } from "@tabler/icons-react"; import { generateCaptcha } from "@/app/captcha"; import { sendOtp, verifyOtp } from "@/app/_util/otp"; export default function SetPreferredNameSimple() { const [preferredName, setPreferredName] = useState(""); const [confirmName, setConfirmName] = useState(""); const [preferredNameError, setPreferredNameError] = useState(null); const [confirmNameError, setConfirmNameError] = useState(null); const [captcha, setCaptcha] = useState(""); const [captchaInput, setCaptchaInput] = useState(""); const [otp, setOtp] = useState(""); const [otpValidated, setOtpValidated] = useState(false); const [countdown, setCountdown] = useState(180); const [timerActive, setTimerActive] = useState(false); const [step, setStep] = useState<"form" | "otp" | "final">("form"); const [existingName, setExistingName] = useState(null); const [loading, setLoading] = useState(true); const icon = ; // Fetch name + generate captcha on mount useEffect(() => { checkPreferredName(); regenerateCaptcha(); }, []); // OTP timer useEffect(() => { let interval: number | undefined; if (timerActive && countdown > 0) { interval = window.setInterval(() => { setCountdown((prev) => prev - 1); }, 1000); } if (countdown === 0) { if (interval) clearInterval(interval); setTimerActive(false); } return () => { if (interval) clearInterval(interval); }; }, [timerActive, countdown]); // API: Fetch preferred name async function checkPreferredName() { try { const token = localStorage.getItem("access_token"); const response = await fetch("/api/auth/user_name", { method: "GET", headers: { "Content-Type": "application/json", "X-Login-Type": "IB", Authorization: `Bearer ${token}`, }, }); const data = await response.json(); if (!response.ok) throw new Error(data.error || "Failed to fetch preferred name"); setExistingName(data.user_name || null); } catch (err) { console.error(err); setExistingName(null); } finally { setLoading(false); } } // Captcha const regenerateCaptcha = async () => { const newCaptcha = await generateCaptcha(); setCaptcha(newCaptcha); setCaptchaInput(""); }; function onPreferredNameChange(value: string) { const sanitized = value.replace(/[^A-Za-z0-9@_]/g, "").slice(0, 11); setPreferredName(sanitized); if (sanitized.length > 0 && (sanitized.length < 5 || sanitized.length > 11)) { setPreferredNameError("Preferred Name must be 5–11 characters."); } else if (!/[A-Za-z]/.test(sanitized)) { setPreferredNameError("Preferred Name must contain at least one letter."); } else if (!/[@_]/.test(sanitized)) { setPreferredNameError("Preferred Name must contain at least one special character (@ or _)."); } else { setPreferredNameError(null); } } function onConfirmNameChange(value: string) { const sanitized = value.replace(/[^A-Za-z0-9@_]/g, "").slice(0, 11); setConfirmName(sanitized); if (sanitized && sanitized !== preferredName) { setConfirmNameError("Confirm name does not match Preferred Name."); } else { setConfirmNameError(null); } } // OTP async function handleSendOtp() { try { await sendOtp({ type: "USERNAME_UPDATED" }); notifications.show({ title: "OTP Sent", message: "An OTP has been sent to your registered mobile.", color: "blue", }); setStep("otp"); setCountdown(180); setTimerActive(true); } catch (err: any) { notifications.show({ title: "Error", message: err.message || "Failed to send OTP.", color: "red", }); } } async function handleVerifyOtp() { try { await verifyOtp(otp); notifications.show({ title: "OTP Verified", message: "OTP has been successfully verified.", color: "green", }); setOtpValidated(true); setStep("final"); } catch { notifications.show({ title: "Invalid OTP", message: "The OTP entered is incorrect.", color: "red", }); } } // Update Preferred Name API async function handleUpdatePreferredName() { const token = localStorage.getItem("access_token"); if (!token) { notifications.show({ title: "Session Expired", message: "Please log in again.", color: "red", }); return; } try { const response = await fetch("/api/auth/user_name", { method: "POST", headers: { "Content-Type": "application/json", "X-Login-Type": "IB", Authorization: `Bearer ${token}`, }, body: JSON.stringify({ user_name: preferredName }), }); const result = await response.json(); if (!response.ok) throw new Error(result.message || "Failed to update preferred name"); notifications.show({ title: "Success", message: "Preferred name updated successfully!", color: "green", }); resetForm(); await checkPreferredName(); // refresh } catch (err: any) { notifications.show({ title: "Error", message: err.message || "Server error, please try again later.", color: "red", }); } } const resetForm = () => { setPreferredName(""); setConfirmName(""); setCaptchaInput(""); setOtp(""); setOtpValidated(false); setStep("form"); regenerateCaptcha(); }; // Main Submit const handleSubmit = async () => { if (step === "form") { if (!preferredName || !confirmName || !captchaInput) { notifications.show({ title: "Missing Fields", message: "Please fill all mandatory fields.", color: "red", }); return; } if(preferredName !== confirmName){ notifications.show({ title: "Mismatch Input", message: "Preferred name and Confirm preferred name not same.", color: "red", }); return; } if (preferredNameError || confirmNameError) { notifications.show({ title: "Invalid Input", message: "Please correct highlighted fields before continuing.", color: "red", }); return; } if (captchaInput !== captcha) { notifications.show({ title: "Invalid Captcha", message: "Please enter correct captcha.", color: "red", }); regenerateCaptcha(); return; } await handleSendOtp(); return; } if (step === "otp") { await handleVerifyOtp(); return; } if (step === "final") { await handleUpdatePreferredName(); return; } }; if (loading) return Loading...; return ( Set Preferred Name
{existingName && ( Current Preferred Name: {existingName} )} {!existingName && ( You have not set the user ID yet. Please set it first. )} onPreferredNameChange(e.currentTarget.value)} withAsterisk mb="xs" maxLength={11} readOnly={step !== "form"} error={preferredNameError || undefined} /> onConfirmNameChange(e.currentTarget.value)} withAsterisk mb="xs" rightSection={icon} readOnly={step !== "form"} onCopy={(e) => e.preventDefault()} onPaste={(e) => e.preventDefault()} onCut={(e) => e.preventDefault()} error={confirmNameError || undefined} /> {/* CAPTCHA */}
{captcha}
setCaptchaInput(e.currentTarget.value)} withAsterisk style={{ flexGrow: 1 }} readOnly={step !== "form"} />
{/* OTP */} {step !== "form" && ( setOtp(e.currentTarget.value)} maxLength={6} withAsterisk disabled={otpValidated} /> {!otpValidated && ( timerActive ? ( Resend OTP in{" "} {String(Math.floor(countdown / 60)).padStart(2, "0")}: {String(countdown % 60).padStart(2, "0")} ) : ( ) )} )}
{/* BUTTONS */} Your Preferred Name must be 5–11 characters long and contain only letters, numbers, and @ or _ symbols. You can change your username a maximum of 5 times. Preferred Name must contain at least one alphabet and one special character (@ or _).
); }