"use client" ;
import { useState } from "react" ;
import { Lock, Delete, Check } from "lucide-react" ;
import { Button } from "@/components/ui/button" ;
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog" ;
import { cn } from "@/lib/utils" ;
const PIN_LENGTH = 4 ;
const CORRECT_PIN = "1234" ;
export const title = "React Dialog Block PIN Entry" ;
export default function DialogPinEntry () {
const [ open , setOpen ] = useState ( false );
const [ pin , setPin ] = useState ( "" );
const [ error , setError ] = useState ( false );
const [ success , setSuccess ] = useState ( false );
const handleDigit = ( digit : string ) => {
if (pin. length < PIN_LENGTH ) {
const newPin = pin + digit;
setPin (newPin);
setError ( false );
if (newPin. length === PIN_LENGTH ) {
if (newPin === CORRECT_PIN ) {
setSuccess ( true );
} else {
setError ( true );
setTimeout (() => setPin ( "" ), 500 );
}
}
}
};
const handleDelete = () => {
setPin (( p ) => p. slice ( 0 , - 1 ));
setError ( false );
};
const handleClose = () => {
setOpen ( false );
setTimeout (() => {
setPin ( "" );
setError ( false );
setSuccess ( false );
}, 200 );
};
return (
< Dialog open = {open} onOpenChange = {setOpen}>
< div className = "flex min-h-[350px] items-center justify-center" >
< DialogTrigger asChild >
< Button variant = "outline" >
< Lock className = "mr-2 h-4 w-4" />
Enter PIN
</ Button >
</ DialogTrigger >
</ div >
< DialogContent style = {{ maxWidth: "320px" }}>
{success ? (
< div className = "flex flex-col items-center justify-center py-8 text-center" >
< Check className = "h-8 w-8 text-primary" />
< h3 className = "mt-4 text-lg font-medium" >Access Granted</ h3 >
< Button onClick = {handleClose} className = "mt-6" >
Continue
</ Button >
</ div >
) : (
<>
< DialogHeader className = "text-center" >
< DialogTitle >Enter PIN</ DialogTitle >
< DialogDescription >
Enter your 4-digit PIN to continue
</ DialogDescription >
</ DialogHeader >
< div className = "py-6 space-y-6" >
{ /* PIN dots */ }
< div
className = { cn (
"flex justify-center gap-3" ,
error && "animate-shake"
)}
>
{Array. from ({ length: PIN_LENGTH }). map (( _ , i ) => (
< div
key = {i}
className = { cn (
"h-3 w-3 rounded-full transition-colors" ,
i < pin. length
? error
? "bg-destructive"
: "bg-primary"
: "bg-muted"
)}
/>
))}
</ div >
{error && (
< p className = "text-center text-sm text-destructive" >
Incorrect PIN. Try again.
</ p >
)}
{ /* Keypad */ }
< div className = "grid grid-cols-3 gap-2" >
{[ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 ]. map (( digit ) => (
< Button
key = {digit}
variant = "outline"
className = "h-12 text-lg font-medium"
onClick = {() => handleDigit ( String (digit))}
>
{digit}
</ Button >
))}
< div />
< Button
variant = "outline"
className = "h-12 text-lg font-medium"
onClick = {() => handleDigit ( "0" )}
>
0
</ Button >
< Button
variant = "ghost"
className = "h-12"
onClick = {handleDelete}
disabled = {pin. length === 0 }
>
< Delete className = "h-5 w-5" />
</ Button >
</ div >
</ div >
</>
)}
</ DialogContent >
</ Dialog >
);
}