kontenhumas-fe/components/auth/otp-form.tsx

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>
);
};