Files
IB/src/app/login/otp/page.tsx
2025-06-25 15:21:16 +05:30

196 lines
6.4 KiB
TypeScript

"use client"
import classes from '../page.module.css';
import React, { useContext, useEffect, useState } from "react";
import { TextInput, Button, Container, Title, Image, SimpleGrid, LoadingOverlay, Avatar, Paper, Text, Group, Center } from "@mantine/core";
import image from '../helpdesk.png';
import { useForm, SubmitHandler } from "react-hook-form";
import axios, { AxiosError } from "axios";
import { notifications } from "@mantine/notifications";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { useDisclosure } from "@mantine/hooks";
type OtpInput = {
OTP: number
}
type Result = {
ok?: boolean
message?: string
}
async function handleResendOTP() {
let otp;
try {
const response = await axios.get("/api/otp");
otp = response.data;
console.log('OTP resent successfully:', response.data);
// Object.assign(response.data);
// alert(response.data.message);
notifications.show({
color: 'green',
// title: error.code,
message: response.data.message
})
} catch (error: AxiosError | any) {
notifications.show({
color: 'red',
title: error.code,
message: error.message
})
}
return otp;
}
async function handleValidateOTP(OtpInput: OtpInput) {
let Result: Result = { ok: false }
try {
const response = await axios.post("/api/otp", OtpInput);
{
window.location.href = '/home'
}
Object.assign(Result, response.data);
} catch (error: AxiosError | any) {
// alert(error.response.data.error);
notifications.show({
withBorder: true,
color: 'red',
title: error.response.status,
message: error.response.data.error,
autoClose: 4000,
})
}
// For countdown reset every time after successful verification
sessionStorage.removeItem('countdown');
sessionStorage.removeItem('timerStart');
return Result;
}
export default function CheckOTP() {
const queryClient = useQueryClient();
const { register, handleSubmit, formState: { errors } } = useForm<OtpInput>();
const [countdown, setCountdown] = useState<number>(90);
const [timerActive, setTimerActive] = useState<boolean>(true);
const validateOTPMutation = useMutation<Result, AxiosError, OtpInput>({
mutationKey: ['validateOtp'],
mutationFn: handleValidateOTP,
})
const resendOTPMutation = useQuery({
queryKey: ['resendOTP'],
queryFn: handleResendOTP,
enabled: false,
})
////
useEffect(() => {
if (typeof window !== 'undefined') {
const savedCountdown = sessionStorage.getItem('countdown');
const savedStartTime = sessionStorage.getItem('timerStart');
if (savedCountdown && savedStartTime) {
const elapsedTime = Math.floor((Date.now() - parseInt(savedStartTime, 10)) / 1000);
const remainingTime = parseInt(savedCountdown, 10) - elapsedTime;
if (remainingTime > 0) {
setCountdown(remainingTime);
setTimerActive(true);
}
else {
setCountdown(0);
setTimerActive(false);
sessionStorage.removeItem('countdown');
sessionStorage.removeItem('timerStart');
}
} else {
sessionStorage.setItem('countdown', '90');
sessionStorage.setItem('timerStart', Date.now().toString());
setCountdown(90);
setTimerActive(true);
}
}
}, []);
// Effect to manage the countdown timer
useEffect(() => {
if (!timerActive) return;
const interval = setInterval(() => {
setCountdown((prev) => {
if (prev <= 1) {
clearInterval(interval);
setTimerActive(false);
sessionStorage.removeItem('countdown');
sessionStorage.removeItem('timerStart');
return 0;
}
return prev - 1;
});
}, 1000);
return () => clearInterval(interval); // Cleanup interval on unmount
}, [timerActive]);
/////
const onSubmit: SubmitHandler<OtpInput> = async (OtpInput) => {
const Result = await validateOTPMutation.mutateAsync(OtpInput);
if (Result.ok)
await queryClient.refetchQueries({ queryKey: ['OTP'] });
};
const handleResend = async () => {
resendOTPMutation.refetch();
setCountdown(90);
setTimerActive(true);
sessionStorage.setItem('countdown', '90');
sessionStorage.setItem('timerStart', Date.now().toString());
}
const [opened, { open, close }] = useDisclosure(false);
return (
<Container className={classes.root}>
<SimpleGrid spacing={{ base: 40, sm: 80 }} cols={{ base: 1, sm: 2 }}>
<Image src={image.src} className={classes.mobileImage} />
<div>
<LoadingOverlay visible={validateOTPMutation.isPending || validateOTPMutation.data?.ok || resendOTPMutation.isLoading} />
<Paper component='form' className={classes.form} radius={0} p={30} onSubmit={handleSubmit(onSubmit)}>
<Avatar
src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcR-ZljkQXsXUP1-EwnIPZtVq_JWhwGUW7b0_eSMno-bag&s"
alt="Jacob Warnhalter"
radius="xxxl"
/>
<Title order={2} className={classes.title} ta="center" mt="md" mb={50}>
Customer Request Management
</Title>
<TextInput label="Enter One-time password(OTP) *"
placeholder="e.g. XXXXX"
size="md"
maxLength={6}
{...register('OTP', { required: true })} />
{errors.OTP && <Text c='red'>*Required</Text>}
<Center mt="lg">
<Text >{timerActive ? `Resend OTP in ${countdown}s` : 'Resend OTP Available'} </Text>
</Center>
<Group justify="center">
<Button mt="xl" size="md" type='submit' >
Validate OTP
</Button>
{validateOTPMutation.data?.message && <Text c='red' ta='center' pt={30}>{validateOTPMutation.data.message}</Text>}
<Button mt="xl" size="md" type='submit' color='blue' onClick={handleResend} disabled={timerActive} >
Resend OTP
</Button>
{/* {resendOTPMutation?.data?.message && <Text c='red' ta='center' pt={30}>{resendOTPMutation.data.message}</Text>} */}
</Group>
</Paper>
</div>
<Image src={image.src} className={classes.desktopImage} />
</SimpleGrid>
<footer justify-content='center' color='green'>Copyright © 2025 Tata Consultancy Services, All rights reserved.</footer>
</Container>
);
}