167 lines
4.5 KiB
TypeScript
167 lines
4.5 KiB
TypeScript
"use client";
|
|
|
|
import React, { useState } from "react";
|
|
import { Button } from "@/components/ui/button";
|
|
import { OTPFormProps } from "@/types/auth";
|
|
import { useOTPVerification } from "@/hooks/use-auth";
|
|
import {
|
|
InputOTP,
|
|
InputOTPGroup,
|
|
InputOTPSeparator,
|
|
InputOTPSlot,
|
|
} from "../ui/input-otp";
|
|
import { useTranslations } from "next-intl";
|
|
|
|
export const OTPForm: React.FC<OTPFormProps> = ({
|
|
loginCredentials,
|
|
onSuccess,
|
|
onError,
|
|
onResend,
|
|
className,
|
|
}) => {
|
|
const { verifyOTP, loading } = useOTPVerification();
|
|
const [otpValue, setOtpValue] = useState("");
|
|
const t = useTranslations("MediaUpdate");
|
|
|
|
const handleTypeOTP = (event: React.KeyboardEvent<HTMLInputElement>) => {
|
|
const { key } = event;
|
|
const target = event.currentTarget;
|
|
|
|
if (key === "Enter") {
|
|
event.preventDefault();
|
|
|
|
const inputs = Array.from(target.form?.querySelectorAll("input") || []);
|
|
const currentIndex = inputs.indexOf(target);
|
|
const nextInput = inputs[currentIndex + 1] as HTMLElement | undefined;
|
|
|
|
if (nextInput) {
|
|
nextInput.focus();
|
|
}
|
|
}
|
|
};
|
|
|
|
const handleOTPChange = (value: string) => {
|
|
setOtpValue(value);
|
|
};
|
|
|
|
const handleSubmit = async () => {
|
|
if (otpValue.length !== 6) {
|
|
onError?.("Please enter a complete 6-digit OTP");
|
|
return;
|
|
}
|
|
|
|
if (!loginCredentials?.username) {
|
|
onError?.("Username not found. Please try logging in again.");
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const isValid = await verifyOTP(loginCredentials.username, otpValue);
|
|
if (isValid) {
|
|
onSuccess?.();
|
|
} else {
|
|
onError?.("Invalid OTP code");
|
|
}
|
|
} catch (error: any) {
|
|
onError?.(error.message || "OTP verification failed");
|
|
}
|
|
};
|
|
|
|
const handleResend = () => {
|
|
onResend?.();
|
|
};
|
|
|
|
return (
|
|
<div className={className}>
|
|
<div className="space-y-6">
|
|
{/* Header */}
|
|
<div className="text-left space-y-2">
|
|
<h1 className="font-semibold text-3xl text-left">{t("enterOTP")}</h1>
|
|
<p className="text-default-500 text-base">{t("enterOTP2")}</p>
|
|
</div>
|
|
|
|
{/* OTP Input */}
|
|
<div className="flex justify-center">
|
|
<InputOTP
|
|
maxLength={6}
|
|
value={otpValue}
|
|
onChange={handleOTPChange}
|
|
disabled={loading}
|
|
className="gap-2"
|
|
>
|
|
<InputOTPGroup>
|
|
<InputOTPSlot
|
|
index={0}
|
|
onKeyDown={handleTypeOTP}
|
|
className="w-12 h-12 text-lg"
|
|
/>
|
|
<InputOTPSlot
|
|
index={1}
|
|
onKeyDown={handleTypeOTP}
|
|
className="w-12 h-12 text-lg"
|
|
/>
|
|
</InputOTPGroup>
|
|
<InputOTPSeparator />
|
|
<InputOTPGroup>
|
|
<InputOTPSlot
|
|
index={2}
|
|
onKeyDown={handleTypeOTP}
|
|
className="w-12 h-12 text-lg"
|
|
/>
|
|
<InputOTPSlot
|
|
index={3}
|
|
onKeyDown={handleTypeOTP}
|
|
className="w-12 h-12 text-lg"
|
|
/>
|
|
</InputOTPGroup>
|
|
<InputOTPSeparator />
|
|
<InputOTPGroup>
|
|
<InputOTPSlot
|
|
index={4}
|
|
onKeyDown={handleTypeOTP}
|
|
className="w-12 h-12 text-lg"
|
|
/>
|
|
<InputOTPSlot
|
|
index={5}
|
|
onKeyDown={handleTypeOTP}
|
|
className="w-12 h-12 text-lg"
|
|
/>
|
|
</InputOTPGroup>
|
|
</InputOTP>
|
|
</div>
|
|
|
|
{/* Resend OTP */}
|
|
<div className="text-center">
|
|
<button
|
|
type="button"
|
|
onClick={handleResend}
|
|
disabled={loading}
|
|
className="text-sm text-blue-600 hover:text-blue-800 underline disabled:opacity-50 disabled:cursor-not-allowed"
|
|
>
|
|
{t("enterOTP3")}
|
|
</button>
|
|
</div>
|
|
|
|
{/* Submit Button */}
|
|
<Button
|
|
type="button"
|
|
fullWidth
|
|
onClick={handleSubmit}
|
|
disabled={otpValue.length !== 6 || loading}
|
|
className="bg-[#C6A455] hover:bg-black"
|
|
color="primary"
|
|
>
|
|
{loading ? (
|
|
<div className="flex items-center gap-2">
|
|
<div className="w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin" />
|
|
|
|
</div>
|
|
) : (
|
|
t("enterOTP4")
|
|
)}
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|